lua-bindings: added Lua bindings to manipulate JSON objects from Lua.
authorKrisztian Litkey <kli@iki.fi>
Fri, 8 Nov 2013 17:12:56 +0000 (19:12 +0200)
committerKrisztian Litkey <kli@iki.fi>
Sat, 9 Nov 2013 00:10:15 +0000 (02:10 +0200)
src/Makefile.am
src/core/lua-bindings/lua-json.c [new file with mode: 0644]
src/core/lua-bindings/lua-json.h [new file with mode: 0644]

index 41dc848..6ac67a1 100644 (file)
@@ -260,13 +260,15 @@ LUA_BINDINGS_SOURCES =                            \
                core/lua-bindings/lua-lua.c     \
                core/lua-bindings/lua-plugin.c  \
                core/lua-bindings/lua-log.c     \
-               core/lua-bindings/lua-console.c
+               core/lua-bindings/lua-console.c \
+               core/lua-bindings/lua-json.c
 
 libmurphy_core_lua_bindings_ladir =                    \
                $(includedir)/murphy/core/lua-bindings
 
 libmurphy_core_lua_bindings_la_HEADERS =               \
-               core/lua-bindings/murphy.h
+               core/lua-bindings/murphy.h              \
+               core/lua-bindings/lua-json.h
 
 libmurphy_coreh_ladir      =           \
                $(includedir)/murphy
diff --git a/src/core/lua-bindings/lua-json.c b/src/core/lua-bindings/lua-json.c
new file mode 100644 (file)
index 0000000..82ee77e
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/json.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-bindings/lua-json.h>
+
+#define JSON_LUA_CLASS MRP_LUA_CLASS(json, lua)
+
+/*
+ * Lua JSON object
+ */
+
+typedef struct {
+    mrp_json_t *json;
+} json_lua_t;
+
+
+/*
+ * Lua JSON object methods
+ */
+
+static void json_lua_destroy(void *data);
+static int  json_lua_getfield(lua_State *L);
+static int  json_lua_setfield(lua_State *L);
+static int  json_lua_stringify(lua_State *L);
+static json_lua_t *json_lua_get(lua_State *L, int idx);
+static mrp_json_t *json_lua_table_to_object(lua_State *L, int t);
+
+
+/*
+ * JSON Lua class
+ */
+
+MRP_LUA_METHOD_LIST_TABLE(json_lua_methods,
+                          MRP_LUA_METHOD_CONSTRUCTOR(mrp_json_lua_create));
+
+MRP_LUA_METHOD_LIST_TABLE(json_lua_overrides,
+                          MRP_LUA_OVERRIDE_CALL     (mrp_json_lua_create)
+                          MRP_LUA_OVERRIDE_GETFIELD (json_lua_getfield)
+                          MRP_LUA_OVERRIDE_SETFIELD (json_lua_setfield)
+                          MRP_LUA_OVERRIDE_STRINGIFY(json_lua_stringify));
+
+MRP_LUA_CLASS_DEF(json, lua, json_lua_t,
+                  json_lua_destroy, json_lua_methods, json_lua_overrides);
+
+
+int mrp_json_lua_create(lua_State *L)
+{
+    json_lua_t *lson;
+    mrp_json_t *json;
+    int         t;
+
+    switch (lua_gettop(L)) {
+    case 0:
+        lson = (json_lua_t *)mrp_lua_create_object(L, JSON_LUA_CLASS, NULL, 0);
+        lson->json = mrp_json_create(MRP_JSON_OBJECT);
+        break;
+
+        /*
+         * assume to be called as m:JSON(<initializer table>)
+         *
+         * Notes: We should check the argument to see if it happens
+         *     to be of type murphy to catch calls of the form m.JSON()
+         *     and redirect them to case 0.
+         */
+    case 1:
+        t = 1;
+    init:
+        if (lua_type(L, t) != LUA_TTABLE)
+            luaL_error(L, "invalid argument to JSON constructor");
+        json = json_lua_table_to_object(L, t);
+        lson = (json_lua_t *)mrp_lua_create_object(L, JSON_LUA_CLASS, NULL, 0);
+        lson->json = json;
+        break;
+
+        /*
+         * assume to be called as m:JSON(<initializer table>), ie.
+         * m.JSON(m, <initializer table>)
+         *
+         * Notes: We should check the first argument and make sure it
+         *     is of type murphy.
+         */
+    case 2:
+        t = 2;
+        goto init;
+
+    default:
+        luaL_error(L, "invalid arguments to JSON constructor (%d)",
+                   lua_gettop(L));
+    }
+
+    mrp_lua_push_object(L, lson);
+
+    return 1;
+}
+
+
+void *mrp_json_lua_wrap(lua_State *L, mrp_json_t *json)
+{
+    json_lua_t *lson = mrp_lua_create_object(L, JSON_LUA_CLASS, NULL, 0);
+
+    lson->json = mrp_json_ref(json);
+
+    return lson;
+}
+
+
+int mrp_json_lua_push(lua_State *L, mrp_json_t *json)
+{
+    json_lua_t *lson;
+
+    if ((lson = mrp_json_lua_wrap(L, json)) != NULL)
+        mrp_lua_push_object(L, lson);
+    else
+        lua_pushnil(L);
+
+    return 1;
+}
+
+
+mrp_json_t *mrp_json_lua_get(lua_State *L, int idx)
+{
+    json_lua_t *lson = json_lua_get(L, idx);
+
+    if (lson != NULL)
+        return mrp_json_ref(lson->json);
+    else
+        return NULL;
+}
+
+
+static void json_lua_destroy(void *data)
+{
+    json_lua_t *lson = (json_lua_t *)data;
+
+    mrp_debug("destroying Lua JSON object %p (%p)", lson, lson->json);
+
+    mrp_json_unref(lson->json);
+}
+
+
+static inline json_lua_t *json_lua_check(lua_State *L, int idx)
+{
+    return (json_lua_t *)mrp_lua_check_object(L, JSON_LUA_CLASS, idx);
+}
+
+
+static json_lua_t *json_lua_get(lua_State *L, int idx)
+{
+    json_lua_t *lson;
+    void       *userdata;
+
+    lua_pushvalue(L, idx);
+    lua_pushliteral(L, "userdata");
+
+    lua_rawget(L, -2);
+    userdata = lua_touserdata(L, -1);
+    lua_pop(L, 2);
+
+    if (userdata != NULL)
+        if ((lson = json_lua_check(L, idx)) != NULL)
+            return lson;
+
+    return NULL;
+}
+
+
+static int json_lua_getfield(lua_State *L)
+{
+    json_lua_t *lson = json_lua_check(L, 1);
+    const char *key;
+    int         idx;
+    mrp_json_t *val;
+
+    switch (lua_type(L, 2)) {
+    case LUA_TSTRING:
+        key = lua_tostring(L, 2);
+        val = mrp_json_get(lson->json, key);
+        break;
+
+    case LUA_TNUMBER:
+        if (mrp_json_get_type(lson->json) != MRP_JSON_ARRAY)
+            luaL_error(L, "trying to index non-array JSON object");
+
+        idx = lua_tointeger(L, 2);
+        val = mrp_json_array_get(lson->json, idx - 1);
+        break;
+
+    default:
+        luaL_error(L, "invalid JSON field/index type (%s).",
+                   lua_typename(L, lua_type(L, 2)));
+    }
+
+    lua_pop(L, 2);
+
+    switch (mrp_json_get_type(val)) {
+    case MRP_JSON_STRING:
+        lua_pushstring(L, mrp_json_string_value(val));
+        break;
+
+    case MRP_JSON_BOOLEAN:
+        lua_pushboolean(L, mrp_json_boolean_value(val));
+        break;
+
+    case MRP_JSON_INTEGER:
+        lua_pushinteger(L, mrp_json_integer_value(val));
+        break;
+
+    case MRP_JSON_DOUBLE:
+        lua_pushnumber(L, mrp_json_double_value(val));
+        break;
+
+    case MRP_JSON_OBJECT:
+    case MRP_JSON_ARRAY:
+        mrp_json_lua_push(L, val);
+        break;
+
+    default:
+        lua_pushnil(L);
+    }
+
+    return 1;
+}
+
+
+static mrp_json_t *json_lua_table_to_object(lua_State *L, int t)
+{
+    json_lua_t *lson;
+    mrp_json_t *json;
+    const char *key;
+    int         idx, i;
+    double      dbl;
+    mrp_json_t *val;
+
+    if ((lson = json_lua_get(L, t)) != NULL)
+        return lson->json;
+
+    json = NULL;
+
+    lua_pushnil(L);
+    while (lua_next(L, t) != 0) {
+        switch (lua_type(L, -2)) {
+        case LUA_TSTRING:
+            if (json != NULL && mrp_json_get_type(json) != MRP_JSON_OBJECT)
+                luaL_error(L, "trying to set member on a JSON array");
+
+            key = lua_tostring(L, -2);
+            idx = 0;
+            break;
+
+        case LUA_TNUMBER:
+            if (json != NULL && mrp_json_get_type(json) != MRP_JSON_ARRAY)
+                luaL_error(L, "trying to set array element on a JSON object");
+
+            if ((idx = lua_tointeger(L, -2)) < 1)
+                luaL_error(L, "invalid index (%d) for JSON array", idx);
+
+            idx--;
+            key = NULL;
+            break;
+
+        default:
+            luaL_error(L, "invalid member (key) for JSON object");
+        }
+
+        switch (lua_type(L, -1)) {
+        case LUA_TSTRING:
+            val = mrp_json_create(MRP_JSON_STRING, lua_tostring(L, -1), -1);
+            break;
+
+        case LUA_TNUMBER:
+            if ((i = lua_tointeger(L, -1)) == (dbl = lua_tonumber(L, -1)))
+                val = mrp_json_create(MRP_JSON_INTEGER, i);
+            else
+                val = mrp_json_create(MRP_JSON_DOUBLE, dbl);
+            break;
+
+        case LUA_TBOOLEAN:
+            val = mrp_json_create(MRP_JSON_BOOLEAN, lua_toboolean(L, -1));
+            break;
+
+        case LUA_TTABLE:
+            val = json_lua_table_to_object(L, lua_gettop(L));
+            break;
+
+        case LUA_TNIL:
+            goto next;
+
+        default:
+            luaL_error(L, "invalid value for JSON member");
+        }
+
+        if (val == NULL) {
+            mrp_json_unref(json);
+            luaL_error(L, "failed convert Lua value to JSON object");
+        }
+
+        if (json == NULL) {
+            json = mrp_json_create(key ? MRP_JSON_OBJECT : MRP_JSON_ARRAY);
+
+            if (json == NULL)
+                luaL_error(L, "failed to create JSON object");
+        }
+
+        if (key != NULL)
+            mrp_json_add(json, key, val);
+        else
+            if (!mrp_json_array_append(json, val))
+                luaL_error(L, "failed to set JSON array element [%d]", idx);
+
+    next:
+        lua_pop(L, 1);
+    }
+
+    return json;
+}
+
+
+static int json_lua_setfield(lua_State *L)
+{
+    json_lua_t *lson = json_lua_check(L, 1);
+    mrp_json_t *json = lson->json;
+    const char *key;
+    int         idx, i;
+    double      dbl;
+    mrp_json_t *val;
+
+    switch (lua_type(L, 2)) {
+    case LUA_TSTRING:
+        if (mrp_json_get_type(json) != MRP_JSON_OBJECT)
+            luaL_error(L, "trying to set member on a JSON array");
+
+        key = lua_tostring(L, 2);
+        idx = 0;
+        break;
+
+    case LUA_TNUMBER:
+        if (mrp_json_get_type(json) != MRP_JSON_ARRAY)
+            luaL_error(L, "trying to set array element on a JSON object");
+
+        if ((idx = lua_tointeger(L, 2)) < 1)
+            luaL_error(L, "invalid index (%d) for JSON array", idx);
+
+        idx--;
+        key = NULL;
+        break;
+
+    default:
+        luaL_error(L, "invalid member (key) for JSON object");
+    }
+
+    switch (lua_type(L, 3)) {
+    case LUA_TSTRING:
+        val = mrp_json_create(MRP_JSON_STRING, lua_tostring(L, 3), -1);
+        break;
+
+    case LUA_TNUMBER:
+        if ((i = lua_tointeger(L, 3)) == (dbl = lua_tonumber(L, 3)))
+            val = mrp_json_create(MRP_JSON_INTEGER, i);
+        else
+            val = mrp_json_create(MRP_JSON_DOUBLE, dbl);
+        break;
+
+    case LUA_TBOOLEAN:
+        val = mrp_json_create(MRP_JSON_BOOLEAN, lua_toboolean(L, 3));
+        break;
+
+    case LUA_TTABLE:
+        if ((val = json_lua_table_to_object(L, 3)) == json)
+            luaL_error(L, "can't set JSON object as a member of itself");
+        break;
+
+    case LUA_TNIL:
+        if (key != NULL)
+            mrp_json_del_member(json, key);
+        else
+            luaL_error(L, "can't delete JSON array element by setting to nil");
+        goto out;
+
+    default:
+        luaL_error(L, "invalid value for JSON member");
+    }
+
+    if (val == NULL)
+        luaL_error(L, "failed convert Lua value to JSON object");
+
+    if (key != NULL)
+        mrp_json_add(json, key, val);
+    else
+        if (!mrp_json_array_set(json, idx, val))
+            luaL_error(L, "failed to set set JSON array element [%d]", idx);
+
+ out:
+    lua_pop(L, 3);
+
+    return 0;
+}
+
+
+static int json_lua_stringify(lua_State *L)
+{
+    json_lua_t *lson = json_lua_check(L, 1);
+
+    lua_pushstring(L, mrp_json_object_to_string(lson->json));
+
+    return 1;
+}
+
+
+MURPHY_REGISTER_LUA_BINDINGS(murphy, JSON_LUA_CLASS,
+                             { "JSON", mrp_json_lua_create });
diff --git a/src/core/lua-bindings/lua-json.h b/src/core/lua-bindings/lua-json.h
new file mode 100644 (file)
index 0000000..0eb4b9b
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_LUA_JSON_H__
+#define __MURPHY_LUA_JSON_H__
+
+#include <murphy/common/json.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+/** Create a Lua JSON object with an empty JSON object. */
+int  mrp_json_lua_create(lua_State *L);
+
+/** Create a Lua JSON object, wrap the given JSON object, increase refcount. */
+void *mrp_json_lua_wrap(lua_State *L, mrp_json_t *json);
+
+/** Wrap the given JSON object, increase refcount and push it on the stack. */
+int mrp_json_lua_push(lua_State *L, mrp_json_t *json);
+
+/** Get the JSON object at the given stack position, increase refcount. */
+mrp_json_t *mrp_json_lua_get(lua_State *L, int idx);
+
+#endif /* __MURPHY_LUA_JSON_H__ */