-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlua-util.h
147 lines (126 loc) · 4.31 KB
/
lua-util.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright 2015 Red Blob Games <[email protected]>
// License: Apache v2.0 <http://www.apache.org/licenses/LICENSE-2.0.html>
/**
* These are some utility functions I wrote while working with the C-Lua API.
*/
#ifndef LUA_UTIL_H
#define LUA_UTIL_H
#include <lua.hpp>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
/**
* Convert a lua expression into a value on the lua stack.
*/
void lua_eval(lua_State* L, const std::string& expr) {
std::string assignment_statement = "_ = " + expr;
int error = luaL_loadstring(L, assignment_statement.c_str()) || lua_pcall(L, 0, 0, 0);
if (error) {
std::cerr << "lua_eval() error " << lua_tostring(L, -1);
lua_pop(L, 1);
lua_pushnil(L);
} else {
lua_getglobal(L, "_");
}
}
/**
* Convert a value on the lua stack to a string representation
* that looks as much as possible like lua source. This should
* leave the lua stack as it found it.
*/
std::string lua_repr(lua_State* L, int index=-1) {
int type = lua_type(L, index);
std::string output;
switch (type) {
case LUA_TNUMBER:
case LUA_TSTRING:
lua_pushvalue(L, index); // stack: ... input
lua_getglobal(L, "string"); // stack: ... input "string"
lua_getfield(L, -1, "format"); // stack: ... input "string" string.format
lua_remove(L, -2); // stack: ... input string.format
if (type == LUA_TNUMBER) {
lua_pushliteral(L, "%g"); // stack: ... input string.format "%g"
} else {
lua_pushliteral(L, "%q"); // stack: ... input string.format "%q"
}
lua_pushvalue(L, -3); // stack: ... input string.format "%q" input
lua_call(L, 2, 1); // stack: ... input output
output = std::string(lua_tostring(L, -1));
lua_pop(L, 2); // stack: ...
return output;
case LUA_TBOOLEAN:
return lua_toboolean(L, index)? "true" : "false";
case LUA_TNIL:
return "nil";
case LUA_TTABLE: {
output = "{";
// Print the array-like elements
lua_len(L, -1); // stack: ... length
lua_Integer array_size = lua_tointeger(L, -1);
lua_pop(L, 1); // stack: ...
for (lua_Integer i = 1; i <= array_size; i++) {
lua_rawgeti(L, -1, i);
if (i > 1) { output += ", "; }
output += lua_repr(L, -1);
lua_pop(L, 1);
}
// Print the record-like elements, but sort them by key for reproducibility
std::vector<std::string> key_value_pairs;
lua_pushvalue(L, index); // stack: ... input
lua_pushnil(L); // stack: ... input nil
while (lua_next(L, -2)) { // stack: ... input key value
if (lua_type(L, -2) == LUA_TNUMBER) {
lua_Integer key = lua_tointeger(L, -2);
if (1 <= key && key <= array_size) {
// Ignore this, as it's already been printed above
lua_pop(L, 1);
continue;
}
}
std::string key = lua_repr(L, -2);
bool shortened_key = false;
if (lua_type(L, -2) == LUA_TSTRING) {
// If it's a name (incomplete test), use the shorter syntax
if (key.size() >= 3
&& key.front() == '"' && key.back() == '"'
&& std::isalpha(key[1])
&& std::all_of(key.begin()+1, key.end()-1, [](char c) { return std::isalnum(c); })) {
key = key.substr(1, key.size()-2);
shortened_key = true;
}
}
std::string pair;
if (!shortened_key) { pair += "["; }
pair += key;
if (!shortened_key) { pair += "]"; }
pair += " = ";
pair += lua_repr(L, -1);
key_value_pairs.push_back(std::move(pair));
lua_pop(L, 1); // stack: ... input key
} // stack: ... input
std::sort(key_value_pairs.begin(), key_value_pairs.end());
for (size_t i = 0; i < key_value_pairs.size(); i++) {
if (array_size != 0 || i > 0) { output += ", "; }
output += key_value_pairs[i];
}
output += "}";
lua_pop(L, 1); // stack: ...
return output;
}
default:
return lua_typename(L, type);
}
}
/**
* Export the lua_repr() function into Lua. Use
*
* lua_pushcfunction(L, export_lua_repr);
* lua_setglobal(L, "repr");
*/
int export_lua_repr(lua_State* L) {
std::string s = lua_repr(L, 1);
lua_pushlstring(L, s.data(), s.size());
return 1;
}
#endif