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 }