lua_parameter_dictionary.cc
Go to the documentation of this file.
1 /*
2  * Copyright 2016 The Cartographer Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // When a LuaParameterDictionary is constructed, a new Lua state (i.e. an
18 // independent Lua interpreter) is fired up to evaluate the Lua code. The code
19 // is expected to return a Lua table that contains key/value pairs that are the
20 // key/value pairs of our parameter dictionary.
21 //
22 // We keep the Lua interpreter around and the table on the stack and reference
23 // it in the Get* methods instead of moving its contents from Lua into a C++ map
24 // since we can only know in the Get* methods what data type to expect in the
25 // table.
26 //
27 // Some of the methods below documentation the current stack with the following
28 // notation: S: <bottom> ... <top>
29 
31 
32 #include <algorithm>
33 #include <cmath>
34 #include <functional>
35 #include <memory>
36 
37 namespace cartographer {
38 namespace common {
39 
40 namespace {
41 
42 // Replace the string at the top of the stack through a quoted version that Lua
43 // can read back.
44 void QuoteStringOnStack(lua_State* L) {
45  CHECK(lua_isstring(L, -1)) << "Top of stack is not a string value.";
46  int current_index = lua_gettop(L);
47 
48  // S: ... string
49  lua_pushglobaltable(L); // S: ... string globals
50  lua_getfield(L, -1, "string"); // S: ... string globals <string module>
51  lua_getfield(L, -1,
52  "format"); // S: ... string globals <string module> format
53  lua_pushstring(L, "%q"); // S: ... string globals <string module> format "%q"
54  lua_pushvalue(L, current_index); // S: ... string globals <string module>
55  // format "%q" string
56 
57  lua_call(L, 2, 1); // S: ... string globals <string module> quoted
58  lua_replace(L, current_index); // S: ... quoted globals <string module>
59 
60  lua_pop(L, 2); // S: ... quoted
61 }
62 
63 // Sets the given 'dictionary' as the value of the "this" key in Lua's registry
64 // table.
65 void SetDictionaryInRegistry(lua_State* L, LuaParameterDictionary* dictionary) {
66  lua_pushstring(L, "this");
67  lua_pushlightuserdata(L, dictionary);
68  lua_settable(L, LUA_REGISTRYINDEX);
69 }
70 
71 // Gets the 'dictionary' from the "this" key in Lua's registry table.
72 LuaParameterDictionary* GetDictionaryFromRegistry(lua_State* L) {
73  lua_pushstring(L, "this");
74  lua_gettable(L, LUA_REGISTRYINDEX);
75  void* return_value = lua_isnil(L, -1) ? nullptr : lua_touserdata(L, -1);
76  lua_pop(L, 1);
77  CHECK(return_value != nullptr);
78  return reinterpret_cast<LuaParameterDictionary*>(return_value);
79 }
80 
81 // CHECK()s if a Lua method returned an error.
82 void CheckForLuaErrors(lua_State* L, int status) {
83  CHECK_EQ(status, 0) << lua_tostring(L, -1);
84 }
85 
86 // Returns 'a' if 'condition' is true, else 'b'.
87 int LuaChoose(lua_State* L) {
88  CHECK_EQ(lua_gettop(L), 3) << "choose() takes (condition, a, b).";
89  CHECK(lua_isboolean(L, 1)) << "condition is not a boolean value.";
90 
91  const bool condition = lua_toboolean(L, 1);
92  if (condition) {
93  lua_pushvalue(L, 2);
94  } else {
95  lua_pushvalue(L, 3);
96  }
97  return 1;
98 }
99 
100 // Pushes a value to the Lua stack.
101 void PushValue(lua_State* L, const int key) { lua_pushinteger(L, key); }
102 void PushValue(lua_State* L, const std::string& key) {
103  lua_pushstring(L, key.c_str());
104 }
105 
106 // Reads the value with the given 'key' from the Lua dictionary and pushes it to
107 // the top of the stack.
108 template <typename T>
109 void GetValueFromLuaTable(lua_State* L, const T& key) {
110  PushValue(L, key);
111  lua_rawget(L, -2);
112 }
113 
114 // CHECK() that the topmost parameter on the Lua stack is a table.
115 void CheckTableIsAtTopOfStack(lua_State* L) {
116  CHECK(lua_istable(L, -1)) << "Topmost item on Lua stack is not a table!";
117 }
118 
119 // Returns true if 'key' is in the table at the top of the Lua stack.
120 template <typename T>
121 bool HasKeyOfType(lua_State* L, const T& key) {
122  CheckTableIsAtTopOfStack(L);
123  PushValue(L, key);
124  lua_rawget(L, -2);
125  const bool key_not_found = lua_isnil(L, -1);
126  lua_pop(L, 1); // Pop the item again.
127  return !key_not_found;
128 }
129 
130 // Iterates over the integer keys of the table at the top of the stack of 'L•
131 // and pushes the values one by one. 'pop_value' is expected to pop a value and
132 // put them into a C++ container.
133 void GetArrayValues(lua_State* L, const std::function<void()>& pop_value) {
134  int idx = 1;
135  while (true) {
136  GetValueFromLuaTable(L, idx);
137  if (lua_isnil(L, -1)) {
138  lua_pop(L, 1);
139  break;
140  }
141  pop_value();
142  ++idx;
143  }
144 }
145 
146 } // namespace
147 
148 std::unique_ptr<LuaParameterDictionary>
150  const std::string& code, std::unique_ptr<FileResolver> file_resolver) {
151  return std::unique_ptr<LuaParameterDictionary>(new LuaParameterDictionary(
152  code, ReferenceCount::NO, std::move(file_resolver)));
153 }
154 
156  const std::string& code, std::unique_ptr<FileResolver> file_resolver)
158  std::move(file_resolver)) {}
159 
161  const std::string& code, ReferenceCount reference_count,
162  std::unique_ptr<FileResolver> file_resolver)
163  : L_(luaL_newstate()),
165  file_resolver_(std::move(file_resolver)),
166  reference_count_(reference_count) {
167  CHECK_NOTNULL(L_);
168  SetDictionaryInRegistry(L_, this);
169 
170  luaL_openlibs(L_);
171 
172  lua_register(L_, "choose", LuaChoose);
173  lua_register(L_, "include", LuaInclude);
174  lua_register(L_, "read", LuaRead);
175 
176  CheckForLuaErrors(L_, luaL_loadstring(L_, code.c_str()));
177  CheckForLuaErrors(L_, lua_pcall(L_, 0, 1, 0));
178  CheckTableIsAtTopOfStack(L_);
179 }
180 
182  lua_State* const L, ReferenceCount reference_count,
183  std::shared_ptr<FileResolver> file_resolver)
184  : L_(lua_newthread(L)),
185  file_resolver_(std::move(file_resolver)),
186  reference_count_(reference_count) {
187  CHECK_NOTNULL(L_);
188 
189  // Make sure this is never garbage collected.
190  CHECK(lua_isthread(L, -1));
191  index_into_reference_table_ = luaL_ref(L, LUA_REGISTRYINDEX);
192 
193  CHECK(lua_istable(L, -1)) << "Topmost item on Lua stack is not a table!";
194  lua_xmove(L, L_, 1); // Moves the table and the coroutine over.
195  CheckTableIsAtTopOfStack(L_);
196 }
197 
201  }
202  if (index_into_reference_table_ > 0) {
203  luaL_unref(L_, LUA_REGISTRYINDEX, index_into_reference_table_);
204  } else {
205  lua_close(L_);
206  }
207 }
208 
209 std::vector<std::string> LuaParameterDictionary::GetKeys() const {
210  CheckTableIsAtTopOfStack(L_);
211  std::vector<std::string> keys;
212 
213  lua_pushnil(L_); // Push the first key
214  while (lua_next(L_, -2) != 0) {
215  lua_pop(L_, 1); // Pop value, keep key.
216  if (!lua_isnumber(L_, -1)) {
217  keys.emplace_back(lua_tostring(L_, -1));
218  }
219  }
220  return keys;
221 }
222 
223 bool LuaParameterDictionary::HasKey(const std::string& key) const {
224  return HasKeyOfType(L_, key);
225 }
226 
227 std::string LuaParameterDictionary::GetString(const std::string& key) {
229  GetValueFromLuaTable(L_, key);
230  return PopString(Quoted::NO);
231 }
232 
233 std::string LuaParameterDictionary::PopString(Quoted quoted) const {
234  CHECK(lua_isstring(L_, -1)) << "Top of stack is not a string value.";
235  if (quoted == Quoted::YES) {
236  QuoteStringOnStack(L_);
237  }
238 
239  const std::string value = lua_tostring(L_, -1);
240  lua_pop(L_, 1);
241  return value;
242 }
243 
244 double LuaParameterDictionary::GetDouble(const std::string& key) {
246  GetValueFromLuaTable(L_, key);
247  return PopDouble();
248 }
249 
251  CHECK(lua_isnumber(L_, -1)) << "Top of stack is not a number value.";
252  const double value = lua_tonumber(L_, -1);
253  lua_pop(L_, 1);
254  return value;
255 }
256 
257 int LuaParameterDictionary::GetInt(const std::string& key) {
259  GetValueFromLuaTable(L_, key);
260  return PopInt();
261 }
262 
264  CHECK(lua_isnumber(L_, -1)) << "Top of stack is not a number value.";
265  const int value = lua_tointeger(L_, -1);
266  lua_pop(L_, 1);
267  return value;
268 }
269 
270 bool LuaParameterDictionary::GetBool(const std::string& key) {
272  GetValueFromLuaTable(L_, key);
273  return PopBool();
274 }
275 
277  CHECK(lua_isboolean(L_, -1)) << "Top of stack is not a boolean value.";
278  const bool value = lua_toboolean(L_, -1);
279  lua_pop(L_, 1);
280  return value;
281 }
282 
283 std::unique_ptr<LuaParameterDictionary> LuaParameterDictionary::GetDictionary(
284  const std::string& key) {
286  GetValueFromLuaTable(L_, key);
288 }
289 
290 std::unique_ptr<LuaParameterDictionary> LuaParameterDictionary::PopDictionary(
291  ReferenceCount reference_count) const {
292  CheckTableIsAtTopOfStack(L_);
293  std::unique_ptr<LuaParameterDictionary> value(
294  new LuaParameterDictionary(L_, reference_count, file_resolver_));
295  // The constructor lua_xmove()s the value, no need to pop it.
296  CheckTableIsAtTopOfStack(L_);
297  return value;
298 }
299 
301  const std::string& indent) const {
302  std::string result = "{";
303  bool dictionary_is_empty = true;
304 
305  const auto top_of_stack_to_string = [this, indent,
306  &dictionary_is_empty]() -> std::string {
307  dictionary_is_empty = false;
308 
309  const int value_type = lua_type(L_, -1);
310  switch (value_type) {
311  case LUA_TBOOLEAN:
312  return PopBool() ? "true" : "false";
313  break;
314  case LUA_TSTRING:
315  return PopString(Quoted::YES);
316  break;
317  case LUA_TNUMBER: {
318  const double value = PopDouble();
319  if (std::isinf(value)) {
320  return value < 0 ? "-math.huge" : "math.huge";
321  } else {
322  return std::to_string(value);
323  }
324  } break;
325  case LUA_TTABLE: {
326  std::unique_ptr<LuaParameterDictionary> subdict(
328  return subdict->DoToString(indent + " ");
329  } break;
330  default:
331  LOG(FATAL) << "Unhandled type " << lua_typename(L_, value_type);
332  }
333  };
334 
335  // Integer (array) keys.
336  for (int i = 1; i; ++i) {
337  GetValueFromLuaTable(L_, i);
338  if (lua_isnil(L_, -1)) {
339  lua_pop(L_, 1);
340  break;
341  }
342  result.append("\n");
343  result.append(indent);
344  result.append(" ");
345  result.append(top_of_stack_to_string());
346  result.append(",");
347  }
348 
349  // String keys.
350  std::vector<std::string> keys = GetKeys();
351  if (!keys.empty()) {
352  std::sort(keys.begin(), keys.end());
353  for (const std::string& key : keys) {
354  GetValueFromLuaTable(L_, key);
355  result.append("\n");
356  result.append(indent);
357  result.append(" ");
358  result.append(key);
359  result.append(" = ");
360  result.append(top_of_stack_to_string());
361  result.append(",");
362  }
363  }
364  result.append("\n");
365  result.append(indent);
366  result.append("}");
367 
368  if (dictionary_is_empty) {
369  return "{}";
370  }
371  return result;
372 }
373 
374 std::string LuaParameterDictionary::ToString() const { return DoToString(""); }
375 
377  std::vector<double> values;
378  GetArrayValues(L_, [&values, this] { values.push_back(PopDouble()); });
379  return values;
380 }
381 
382 std::vector<std::unique_ptr<LuaParameterDictionary>>
384  std::vector<std::unique_ptr<LuaParameterDictionary>> values;
385  GetArrayValues(L_, [&values, this] {
386  values.push_back(PopDictionary(reference_count_));
387  });
388  return values;
389 }
390 
392  std::vector<std::string> values;
393  GetArrayValues(L_,
394  [&values, this] { values.push_back(PopString(Quoted::NO)); });
395  return values;
396 }
397 
398 void LuaParameterDictionary::CheckHasKey(const std::string& key) const {
399  CHECK(HasKey(key)) << "Key '" << key << "' not in dictionary:\n"
400  << ToString();
401 }
402 
404  CheckHasKey(key);
405  reference_counts_[key]++;
406 }
407 
409  for (const auto& key : GetKeys()) {
410  CHECK_EQ(1, reference_counts_.count(key))
411  << "Key '" << key << "' was used the wrong number of times.";
412  CHECK_EQ(1, reference_counts_.at(key))
413  << "Key '" << key << "' was used the wrong number of times.";
414  }
415  reference_counts_.clear();
416 }
417 
418 int LuaParameterDictionary::GetNonNegativeInt(const std::string& key) {
419  const int temp = GetInt(key); // Will increase reference count.
420  CHECK_GE(temp, 0) << temp << " is negative.";
421  return temp;
422 }
423 
424 // Lua function to run a script in the current Lua context. Takes the filename
425 // as its only argument.
427  CHECK_EQ(lua_gettop(L), 1);
428  CHECK(lua_isstring(L, -1)) << "include takes a filename.";
429 
430  LuaParameterDictionary* parameter_dictionary = GetDictionaryFromRegistry(L);
431  const std::string basename = lua_tostring(L, -1);
432  const std::string filename =
433  parameter_dictionary->file_resolver_->GetFullPathOrDie(basename);
434  if (std::find(parameter_dictionary->included_files_.begin(),
435  parameter_dictionary->included_files_.end(),
436  filename) != parameter_dictionary->included_files_.end()) {
437  std::string error_msg =
438  "Tried to include " + filename +
439  " twice. Already included files in order of inclusion: ";
440  for (const std::string& filename : parameter_dictionary->included_files_) {
441  error_msg.append(filename);
442  error_msg.append("\n");
443  }
444  LOG(FATAL) << error_msg;
445  }
446  parameter_dictionary->included_files_.push_back(filename);
447  lua_pop(L, 1);
448  CHECK_EQ(lua_gettop(L), 0);
449 
450  const std::string content =
451  parameter_dictionary->file_resolver_->GetFileContentOrDie(basename);
452  CheckForLuaErrors(
453  L, luaL_loadbuffer(L, content.c_str(), content.size(), filename.c_str()));
454  CheckForLuaErrors(L, lua_pcall(L, 0, LUA_MULTRET, 0));
455 
456  return lua_gettop(L);
457 }
458 
459 // Lua function to read a file into a string.
461  CHECK_EQ(lua_gettop(L), 1);
462  CHECK(lua_isstring(L, -1)) << "read takes a filename.";
463 
464  LuaParameterDictionary* parameter_dictionary = GetDictionaryFromRegistry(L);
465  const std::string file_content =
466  parameter_dictionary->file_resolver_->GetFileContentOrDie(
467  lua_tostring(L, -1));
468  lua_pushstring(L, file_content.c_str());
469  return 1;
470 }
471 
472 } // namespace common
473 } // namespace cartographer
std::string DoToString(const std::string &indent) const
const std::shared_ptr< FileResolver > file_resolver_
LuaParameterDictionary(const std::string &code, std::unique_ptr< FileResolver > file_resolver)
std::vector< std::unique_ptr< LuaParameterDictionary > > GetArrayValuesAsDictionaries()
static std::unique_ptr< LuaParameterDictionary > NonReferenceCounted(const std::string &code, std::unique_ptr< FileResolver > file_resolver)
std::unique_ptr< LuaParameterDictionary > PopDictionary(ReferenceCount reference_count) const
std::unique_ptr< LuaParameterDictionary > GetDictionary(const std::string &key)


cartographer
Author(s): The Cartographer Authors
autogenerated on Mon Feb 28 2022 22:00:58