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);
49 lua_pushglobaltable(L);
50 lua_getfield(L, -1,
"string");
53 lua_pushstring(L,
"%q");
54 lua_pushvalue(L, current_index);
58 lua_replace(L, current_index);
65 void SetDictionaryInRegistry(lua_State* L, LuaParameterDictionary* dictionary) {
66 lua_pushstring(L,
"this");
67 lua_pushlightuserdata(L, dictionary);
68 lua_settable(L, LUA_REGISTRYINDEX);
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);
77 CHECK(return_value !=
nullptr);
78 return reinterpret_cast<LuaParameterDictionary*
>(return_value);
82 void CheckForLuaErrors(lua_State* L,
int status) {
83 CHECK_EQ(status, 0) << lua_tostring(L, -1);
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.";
91 const bool condition = lua_toboolean(L, 1);
101 void PushValue(lua_State* L,
const int key) { lua_pushinteger(L, key); }
102 void PushValue(lua_State* L,
const string& key) {
103 lua_pushstring(L, key.c_str());
108 template <
typename T>
109 void GetValueFromLuaTable(lua_State* L,
const T& key) {
115 void CheckTableIsAtTopOfStack(lua_State* L) {
116 CHECK(lua_istable(L, -1)) <<
"Topmost item on Lua stack is not a table!";
120 template <
typename T>
121 bool HasKeyOfType(lua_State* L,
const T& key) {
122 CheckTableIsAtTopOfStack(L);
125 const bool key_not_found = lua_isnil(L, -1);
127 return !key_not_found;
133 void GetArrayValues(lua_State* L,
const std::function<
void()>& pop_value) {
136 GetValueFromLuaTable(L, idx);
137 if (lua_isnil(L, -1)) {
148 std::unique_ptr<LuaParameterDictionary>
150 const string& code, std::unique_ptr<FileResolver> file_resolver) {
156 const string& code, std::unique_ptr<FileResolver> file_resolver)
158 std::move(file_resolver)) {}
162 std::unique_ptr<FileResolver> file_resolver)
163 :
L_(luaL_newstate()),
168 SetDictionaryInRegistry(
L_,
this);
172 lua_register(
L_,
"choose", LuaChoose);
176 CheckForLuaErrors(
L_, luaL_loadstring(
L_, code.c_str()));
177 CheckForLuaErrors(
L_, lua_pcall(
L_, 0, 1, 0));
178 CheckTableIsAtTopOfStack(
L_);
183 std::shared_ptr<FileResolver> file_resolver)
184 :
L_(lua_newthread(L)),
190 CHECK(lua_isthread(L, -1));
193 CHECK(lua_istable(L, -1)) <<
"Topmost item on Lua stack is not a table!";
195 CheckTableIsAtTopOfStack(
L_);
210 CheckTableIsAtTopOfStack(
L_);
211 std::vector<string> keys;
214 while (lua_next(
L_, -2) != 0) {
216 if (!lua_isnumber(
L_, -1)) {
217 keys.emplace_back(lua_tostring(
L_, -1));
224 return HasKeyOfType(
L_, key);
229 GetValueFromLuaTable(
L_, key);
234 CHECK(lua_isstring(
L_, -1)) <<
"Top of stack is not a string value.";
236 QuoteStringOnStack(
L_);
239 const string value = lua_tostring(
L_, -1);
246 GetValueFromLuaTable(
L_, key);
251 CHECK(lua_isnumber(
L_, -1)) <<
"Top of stack is not a number value.";
252 const double value = lua_tonumber(
L_, -1);
259 GetValueFromLuaTable(
L_, key);
264 CHECK(lua_isnumber(
L_, -1)) <<
"Top of stack is not a number value.";
265 const int value = lua_tointeger(
L_, -1);
272 GetValueFromLuaTable(
L_, key);
277 CHECK(lua_isboolean(
L_, -1)) <<
"Top of stack is not a boolean value.";
278 const bool value = lua_toboolean(
L_, -1);
286 GetValueFromLuaTable(
L_, key);
292 CheckTableIsAtTopOfStack(
L_);
293 std::unique_ptr<LuaParameterDictionary>
value(
296 CheckTableIsAtTopOfStack(
L_);
302 bool dictionary_is_empty =
true;
304 const auto top_of_stack_to_string = [
this, indent,
305 &dictionary_is_empty]() ->
string {
306 dictionary_is_empty =
false;
308 const int value_type = lua_type(
L_, -1);
309 switch (value_type) {
311 return PopBool() ?
"true" :
"false";
318 if (std::isinf(value)) {
319 return value < 0 ?
"-math.huge" :
"math.huge";
321 return std::to_string(value);
325 std::unique_ptr<LuaParameterDictionary> subdict(
327 return subdict->DoToString(indent +
" ");
330 LOG(FATAL) <<
"Unhandled type " << lua_typename(
L_, value_type);
335 for (
int i = 1; i; ++i) {
336 GetValueFromLuaTable(
L_, i);
337 if (lua_isnil(
L_, -1)) {
342 result.append(indent);
344 result.append(top_of_stack_to_string());
349 std::vector<string> keys =
GetKeys();
351 std::sort(keys.begin(), keys.end());
352 for (
const string& key : keys) {
353 GetValueFromLuaTable(
L_, key);
355 result.append(indent);
358 result.append(
" = ");
359 result.append(top_of_stack_to_string());
364 result.append(indent);
367 if (dictionary_is_empty) {
376 std::vector<double> values;
377 GetArrayValues(
L_, [&values,
this] { values.push_back(
PopDouble()); });
381 std::vector<std::unique_ptr<LuaParameterDictionary>>
383 std::vector<std::unique_ptr<LuaParameterDictionary>> values;
384 GetArrayValues(
L_, [&values,
this] {
391 std::vector<string> values;
398 CHECK(
HasKey(key)) <<
"Key '" << key <<
"' not in dictionary:\n" 408 for (
const auto& key :
GetKeys()) {
410 <<
"Key '" << key <<
"' was used the wrong number of times.";
412 <<
"Key '" << key <<
"' was used the wrong number of times.";
418 const int temp =
GetInt(key);
419 CHECK_GE(temp, 0) << temp <<
" is negative.";
426 CHECK_EQ(lua_gettop(L), 1);
427 CHECK(lua_isstring(L, -1)) <<
"include takes a filename.";
430 const string basename = lua_tostring(L, -1);
431 const string filename =
436 string error_msg =
"Tried to include " + filename +
437 " twice. Already included files in order of inclusion: ";
439 error_msg.append(filename);
440 error_msg.append(
"\n");
442 LOG(FATAL) << error_msg;
446 CHECK_EQ(lua_gettop(L), 0);
448 const string content =
449 parameter_dictionary->
file_resolver_->GetFileContentOrDie(basename);
451 L, luaL_loadbuffer(L, content.c_str(), content.size(), filename.c_str()));
452 CheckForLuaErrors(L, lua_pcall(L, 0, LUA_MULTRET, 0));
454 return lua_gettop(L);
459 CHECK_EQ(lua_gettop(L), 1);
460 CHECK(lua_isstring(L, -1)) <<
"read takes a filename.";
463 const string file_content =
465 lua_tostring(L, -1));
466 lua_pushstring(L, file_content.c_str());
std::unique_ptr< LuaParameterDictionary > PopDictionary(ReferenceCount reference_count) const
bool GetBool(const string &key)
int GetNonNegativeInt(const string &key)
std::vector< double > GetArrayValuesAsDoubles()
const std::shared_ptr< FileResolver > file_resolver_
std::map< string, int > reference_counts_
std::vector< string > GetArrayValuesAsStrings()
int GetInt(const string &key)
std::vector< string > included_files_
std::vector< std::unique_ptr< LuaParameterDictionary > > GetArrayValuesAsDictionaries()
double GetDouble(const string &key)
void CheckAllKeysWereUsedExactlyOnceAndReset()
int index_into_reference_table_
static int LuaInclude(lua_State *L)
LuaParameterDictionary(const string &code, std::unique_ptr< FileResolver > file_resolver)
static int LuaRead(lua_State *L)
const ReferenceCount reference_count_
string PopString(Quoted quoted) const
bool HasKey(const string &key) const
std::vector< string > GetKeys() const
static std::unique_ptr< LuaParameterDictionary > NonReferenceCounted(const string &code, std::unique_ptr< FileResolver > file_resolver)
string GetString(const string &key)
void CheckHasKey(const string &key) const
string DoToString(const string &indent) const
std::unique_ptr< LuaParameterDictionary > GetDictionary(const string &key)
void CheckHasKeyAndReference(const string &key)
~LuaParameterDictionary()