diff 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
line wrap: on
line diff
--- a/Core/Lua/LuaFunctionCall.cpp	Thu Jul 03 16:27:16 2014 +0200
+++ b/Core/Lua/LuaFunctionCall.cpp	Thu Jul 03 18:12:50 2014 +0200
@@ -34,7 +34,9 @@
 #include "LuaFunctionCall.h"
 
 #include <cassert>
-
+#include <stdio.h>
+#include <boost/lexical_cast.hpp>
+#include <glog/logging.h>
 
 namespace Orthanc
 {
@@ -80,7 +82,7 @@
     lua_pushnumber(context_.lua_, value);
   }
 
-  void LuaFunctionCall::PushJSON(const Json::Value& value)
+  void LuaFunctionCall::PushJson(const Json::Value& value)
   {
     CheckAlreadyExecuted();
 
@@ -119,7 +121,7 @@
         lua_pushnumber(context_.lua_, i + 1);
 
         // Push the value of the cell
-        PushJSON(value[i]);
+        PushJson(value[i]);
 
         // Stores the pair in the table
         lua_rawset(context_.lua_, -3);
@@ -138,7 +140,7 @@
         lua_pushstring(context_.lua_, it->c_str());
 
         // Push the value of the cell
-        PushJSON(value[*it]);
+        PushJson(value[*it]);
 
         // Stores the pair in the table
         lua_rawset(context_.lua_, -3);
@@ -150,7 +152,7 @@
     }
   }
 
-  void LuaFunctionCall::Execute(int numOutputs)
+  void LuaFunctionCall::ExecuteInternal(int numOutputs)
   {
     CheckAlreadyExecuted();
 
@@ -177,13 +179,8 @@
 
   bool LuaFunctionCall::ExecutePredicate()
   {
-    Execute(1);
-        
-    if (lua_gettop(context_.lua_) == 0)
-    {
-      throw LuaException("No output was provided by the function");
-    }
-
+    ExecuteInternal(1);
+    
     if (!lua_isboolean(context_.lua_, 1))
     {
       throw LuaException("The function is not a predicate (only true/false outputs allowed)");
@@ -191,4 +188,95 @@
 
     return lua_toboolean(context_.lua_, 1) != 0;
   }
+
+
+  static void PopJson(Json::Value& result,
+                      lua_State* lua,
+                      int top)
+  {
+    if (lua_istable(lua, top))
+    {
+      Json::Value tmp = Json::objectValue;
+      bool isArray = true;
+      size_t size = 0;
+
+      // http://stackoverflow.com/a/6142700/881731
+      
+      // Push another reference to the table on top of the stack (so we know
+      // where it is, and this function can work for negative, positive and
+      // pseudo indices
+      lua_pushvalue(lua, top);
+      // stack now contains: -1 => table
+      lua_pushnil(lua);
+      // stack now contains: -1 => nil; -2 => table
+      while (lua_next(lua, -2))
+      {
+        // stack now contains: -1 => value; -2 => key; -3 => table
+        // copy the key so that lua_tostring does not modify the original
+        lua_pushvalue(lua, -2);
+        // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table
+        std::string key(lua_tostring(lua, -1));
+        Json::Value v;
+        PopJson(v, lua, -2);
+
+        tmp[key] = v;
+
+        size += 1;
+        try
+        {
+          if (boost::lexical_cast<size_t>(key) != size)
+          {
+            isArray = false;
+          }
+        }
+        catch (boost::bad_lexical_cast&)
+        {
+          isArray = false;
+        }
+        
+        // pop value + copy of key, leaving original key
+        lua_pop(lua, 2);
+        // stack now contains: -1 => key; -2 => table
+      }
+      // stack now contains: -1 => table (when lua_next returns 0 it pops the key
+      // but does not push anything.)
+      // Pop table
+      lua_pop(lua, 1);
+
+      // Stack is now the same as it was on entry to this function
+
+      if (isArray)
+      {
+        result = Json::arrayValue;
+        for (size_t i = 0; i < size; i++)
+        {
+          result.append(tmp[boost::lexical_cast<std::string>(i + 1)]);
+        }
+      }
+      else
+      {
+        result = tmp;
+      }
+    }
+    else if (lua_isnumber(lua, top))
+    {
+      result = static_cast<float>(lua_tonumber(lua, top));
+    }
+    else if (lua_isstring(lua, top))
+    {
+      result = std::string(lua_tostring(lua, top));
+    }
+    else
+    {
+      LOG(WARNING) << "Unsupported Lua type when returning Json";
+      result = Json::nullValue;
+    }
+  }
+
+
+  void LuaFunctionCall::ExecuteToJson(Json::Value& result)
+  {
+    ExecuteInternal(1);
+    PopJson(result, context_.lua_, lua_gettop(context_.lua_));
+  }
 }