Mercurial > hg > orthanc
comparison Core/Lua/LuaFunctionCall.cpp @ 997:1b1d51e9f1a2 lua-scripting
return Json from Lua
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 03 Jul 2014 18:12:50 +0200 |
parents | cf52f3bcb2b3 |
children | 160dfe770618 |
comparison
equal
deleted
inserted
replaced
996:cf52f3bcb2b3 | 997:1b1d51e9f1a2 |
---|---|
32 | 32 |
33 #include "../PrecompiledHeaders.h" | 33 #include "../PrecompiledHeaders.h" |
34 #include "LuaFunctionCall.h" | 34 #include "LuaFunctionCall.h" |
35 | 35 |
36 #include <cassert> | 36 #include <cassert> |
37 | 37 #include <stdio.h> |
38 #include <boost/lexical_cast.hpp> | |
39 #include <glog/logging.h> | |
38 | 40 |
39 namespace Orthanc | 41 namespace Orthanc |
40 { | 42 { |
41 void LuaFunctionCall::CheckAlreadyExecuted() | 43 void LuaFunctionCall::CheckAlreadyExecuted() |
42 { | 44 { |
78 { | 80 { |
79 CheckAlreadyExecuted(); | 81 CheckAlreadyExecuted(); |
80 lua_pushnumber(context_.lua_, value); | 82 lua_pushnumber(context_.lua_, value); |
81 } | 83 } |
82 | 84 |
83 void LuaFunctionCall::PushJSON(const Json::Value& value) | 85 void LuaFunctionCall::PushJson(const Json::Value& value) |
84 { | 86 { |
85 CheckAlreadyExecuted(); | 87 CheckAlreadyExecuted(); |
86 | 88 |
87 if (value.isString()) | 89 if (value.isString()) |
88 { | 90 { |
117 { | 119 { |
118 // Push the table index (note the "+1" because of Lua conventions) | 120 // Push the table index (note the "+1" because of Lua conventions) |
119 lua_pushnumber(context_.lua_, i + 1); | 121 lua_pushnumber(context_.lua_, i + 1); |
120 | 122 |
121 // Push the value of the cell | 123 // Push the value of the cell |
122 PushJSON(value[i]); | 124 PushJson(value[i]); |
123 | 125 |
124 // Stores the pair in the table | 126 // Stores the pair in the table |
125 lua_rawset(context_.lua_, -3); | 127 lua_rawset(context_.lua_, -3); |
126 } | 128 } |
127 } | 129 } |
136 { | 138 { |
137 // Push the index of the cell | 139 // Push the index of the cell |
138 lua_pushstring(context_.lua_, it->c_str()); | 140 lua_pushstring(context_.lua_, it->c_str()); |
139 | 141 |
140 // Push the value of the cell | 142 // Push the value of the cell |
141 PushJSON(value[*it]); | 143 PushJson(value[*it]); |
142 | 144 |
143 // Stores the pair in the table | 145 // Stores the pair in the table |
144 lua_rawset(context_.lua_, -3); | 146 lua_rawset(context_.lua_, -3); |
145 } | 147 } |
146 } | 148 } |
148 { | 150 { |
149 throw LuaException("Unsupported JSON conversion"); | 151 throw LuaException("Unsupported JSON conversion"); |
150 } | 152 } |
151 } | 153 } |
152 | 154 |
153 void LuaFunctionCall::Execute(int numOutputs) | 155 void LuaFunctionCall::ExecuteInternal(int numOutputs) |
154 { | 156 { |
155 CheckAlreadyExecuted(); | 157 CheckAlreadyExecuted(); |
156 | 158 |
157 assert(lua_gettop(context_.lua_) >= 1); | 159 assert(lua_gettop(context_.lua_) >= 1); |
158 int nargs = lua_gettop(context_.lua_) - 1; | 160 int nargs = lua_gettop(context_.lua_) - 1; |
175 isExecuted_ = true; | 177 isExecuted_ = true; |
176 } | 178 } |
177 | 179 |
178 bool LuaFunctionCall::ExecutePredicate() | 180 bool LuaFunctionCall::ExecutePredicate() |
179 { | 181 { |
180 Execute(1); | 182 ExecuteInternal(1); |
183 | |
184 if (!lua_isboolean(context_.lua_, 1)) | |
185 { | |
186 throw LuaException("The function is not a predicate (only true/false outputs allowed)"); | |
187 } | |
188 | |
189 return lua_toboolean(context_.lua_, 1) != 0; | |
190 } | |
191 | |
192 | |
193 static void PopJson(Json::Value& result, | |
194 lua_State* lua, | |
195 int top) | |
196 { | |
197 if (lua_istable(lua, top)) | |
198 { | |
199 Json::Value tmp = Json::objectValue; | |
200 bool isArray = true; | |
201 size_t size = 0; | |
202 | |
203 // http://stackoverflow.com/a/6142700/881731 | |
204 | |
205 // Push another reference to the table on top of the stack (so we know | |
206 // where it is, and this function can work for negative, positive and | |
207 // pseudo indices | |
208 lua_pushvalue(lua, top); | |
209 // stack now contains: -1 => table | |
210 lua_pushnil(lua); | |
211 // stack now contains: -1 => nil; -2 => table | |
212 while (lua_next(lua, -2)) | |
213 { | |
214 // stack now contains: -1 => value; -2 => key; -3 => table | |
215 // copy the key so that lua_tostring does not modify the original | |
216 lua_pushvalue(lua, -2); | |
217 // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table | |
218 std::string key(lua_tostring(lua, -1)); | |
219 Json::Value v; | |
220 PopJson(v, lua, -2); | |
221 | |
222 tmp[key] = v; | |
223 | |
224 size += 1; | |
225 try | |
226 { | |
227 if (boost::lexical_cast<size_t>(key) != size) | |
228 { | |
229 isArray = false; | |
230 } | |
231 } | |
232 catch (boost::bad_lexical_cast&) | |
233 { | |
234 isArray = false; | |
235 } | |
181 | 236 |
182 if (lua_gettop(context_.lua_) == 0) | 237 // pop value + copy of key, leaving original key |
183 { | 238 lua_pop(lua, 2); |
184 throw LuaException("No output was provided by the function"); | 239 // stack now contains: -1 => key; -2 => table |
185 } | 240 } |
186 | 241 // stack now contains: -1 => table (when lua_next returns 0 it pops the key |
187 if (!lua_isboolean(context_.lua_, 1)) | 242 // but does not push anything.) |
188 { | 243 // Pop table |
189 throw LuaException("The function is not a predicate (only true/false outputs allowed)"); | 244 lua_pop(lua, 1); |
190 } | 245 |
191 | 246 // Stack is now the same as it was on entry to this function |
192 return lua_toboolean(context_.lua_, 1) != 0; | 247 |
248 if (isArray) | |
249 { | |
250 result = Json::arrayValue; | |
251 for (size_t i = 0; i < size; i++) | |
252 { | |
253 result.append(tmp[boost::lexical_cast<std::string>(i + 1)]); | |
254 } | |
255 } | |
256 else | |
257 { | |
258 result = tmp; | |
259 } | |
260 } | |
261 else if (lua_isnumber(lua, top)) | |
262 { | |
263 result = static_cast<float>(lua_tonumber(lua, top)); | |
264 } | |
265 else if (lua_isstring(lua, top)) | |
266 { | |
267 result = std::string(lua_tostring(lua, top)); | |
268 } | |
269 else | |
270 { | |
271 LOG(WARNING) << "Unsupported Lua type when returning Json"; | |
272 result = Json::nullValue; | |
273 } | |
274 } | |
275 | |
276 | |
277 void LuaFunctionCall::ExecuteToJson(Json::Value& result) | |
278 { | |
279 ExecuteInternal(1); | |
280 PopJson(result, context_.lua_, lua_gettop(context_.lua_)); | |
193 } | 281 } |
194 } | 282 } |