webmcp
view libraries/json/json.c @ 132:3cafdf1143f3
Bugfix in json.type(...) function: Return nil if path is nonexistent, but return string "nil" if only last element is nil
| author | jbe | 
|---|---|
| date | Sun Jul 27 22:02:37 2014 +0200 (2014-07-27) | 
| parents | dbe5881e4ecd | 
| children | 214e11b72907 | 
 line source
     1 #include <lua.h>
     2 #include <lauxlib.h>
     3 #include <stdlib.h>
     4 #include <string.h>
     6 #define JSON_UPVAL_NULLMARK  lua_upvalueindex(1)
     7 #define JSON_UPVAL_SHADOWTBL lua_upvalueindex(2)
     8 #define JSON_UPVAL_TYPES     lua_upvalueindex(3)
     9 #define JSON_UPVAL_METATABLE lua_upvalueindex(4)
    11 #define JSON_STATE_VALUE 0
    12 #define JSON_STATE_OBJECT_KEY 1
    13 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2
    14 #define JSON_STATE_OBJECT_VALUE 3
    15 #define JSON_STATE_OBJECT_SEPARATOR 4
    16 #define JSON_STATE_ARRAY_VALUE 5
    17 #define JSON_STATE_ARRAY_SEPARATOR 6
    18 #define JSON_STATE_END 7
    20 static int json_import(lua_State *L) {
    21   const char *str;
    22   size_t total;
    23   size_t pos = 0;
    24   size_t level = 0;
    25   int mode = JSON_STATE_VALUE;
    26   char c;
    27   luaL_Buffer luabuf;
    28   char *cbuf;
    29   size_t writepos;
    30   lua_settop(L, 1);
    31   str = lua_tostring(L, 1);
    32   total = strlen(str);
    33 json_import_loop:
    34   while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++;
    35   switch (c) {
    36   case 0:
    37     if (mode == JSON_STATE_END) return 1;
    38     json_import_unexpected_eof:
    39     lua_pushnil(L);
    40     if (level == 0) lua_pushliteral(L, "Empty string");
    41     else lua_pushliteral(L, "Unexpected end of JSON document");
    42     return 2;
    43   case '{':
    44     if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE)
    45       goto json_import_syntax_error;
    46     pos++;
    47     lua_newtable(L);  // the external JSON object representation
    48     lua_pushvalue(L, JSON_UPVAL_METATABLE);
    49     lua_setmetatable(L, -2);
    50     lua_pushvalue(L, -1);
    51     lua_pushliteral(L, "object");
    52     lua_rawset(L, JSON_UPVAL_TYPES);
    53     lua_newtable(L);  // the internal shadow table
    54     lua_pushvalue(L, -2);
    55     lua_pushvalue(L, -2);
    56     lua_rawset(L, JSON_UPVAL_SHADOWTBL);
    57     mode = JSON_STATE_OBJECT_KEY;
    58     level++;
    59     goto json_import_loop;
    60   case '[':
    61     if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE)
    62       goto json_import_syntax_error;
    63     pos++;
    64     lua_newtable(L);  // the external JSON array representation
    65     lua_pushvalue(L, JSON_UPVAL_METATABLE);
    66     lua_setmetatable(L, -2);
    67     lua_pushvalue(L, -1);
    68     lua_pushliteral(L, "array");
    69     lua_rawset(L, JSON_UPVAL_TYPES);
    70     lua_newtable(L);  // the internal shadow table
    71     lua_pushvalue(L, -2);
    72     lua_pushvalue(L, -2);
    73     lua_rawset(L, JSON_UPVAL_SHADOWTBL);
    74     lua_pushinteger(L, 0);  // magic integer to indicate an array
    75     mode = JSON_STATE_ARRAY_VALUE;
    76     level++;
    77     goto json_import_loop;
    78   case '}':
    79     if (mode != JSON_STATE_OBJECT_KEY && mode != JSON_STATE_OBJECT_SEPARATOR)
    80       goto json_import_syntax_error;
    81     goto json_import_close;
    82   case ']':
    83     if (mode != JSON_STATE_ARRAY_VALUE && mode != JSON_STATE_ARRAY_SEPARATOR)
    84       goto json_import_syntax_error;
    85     lua_pop(L, 1);  // pop magic integer
    86   json_import_close:
    87     pos++;
    88     lua_pop(L, 1);  // pop shadow table
    89     if (--level) {
    90       if (lua_type(L, -2) == LUA_TNUMBER) {
    91         mode = JSON_STATE_ARRAY_VALUE;
    92       } else {
    93         mode = JSON_STATE_OBJECT_VALUE;
    94       }
    95       goto json_import_process_value;
    96     } else {
    97       mode = JSON_STATE_END;
    98     }
    99     goto json_import_loop;
   100   case ':':
   101     if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR)
   102       goto json_import_syntax_error;
   103     pos++;
   104     mode = JSON_STATE_OBJECT_VALUE;
   105     goto json_import_loop;
   106   case ',':
   107     if (mode == JSON_STATE_OBJECT_SEPARATOR) {
   108       mode = JSON_STATE_OBJECT_KEY;
   109     } else if (mode == JSON_STATE_ARRAY_SEPARATOR) {
   110       mode = JSON_STATE_ARRAY_VALUE;
   111     } else {
   112       goto json_import_syntax_error;
   113     }
   114     pos++;
   115     goto json_import_loop;
   116   case '"':
   117     cbuf = luaL_buffinitsize(L, &luabuf, total-pos);
   118     writepos = 0;
   119     pos++;
   120     while ((c = str[pos++]) != '"') {
   121       if (c == 0) {
   122         goto json_import_unexpected_eof;
   123       } else if (c < 32 || c == 127) {
   124         lua_pushnil(L);
   125         lua_pushliteral(L, "Unexpected control character in JSON string");
   126         return 2;
   127       } else if (c == '\\') {
   128         c = str[pos++];
   129         switch (c) {
   130         case 0:
   131           goto json_import_unexpected_eof;
   132         case '"':
   133         case '/':
   134         case '\\':
   135           cbuf[writepos++] = c;
   136           break;
   137         case 'b':
   138           cbuf[writepos++] = '\b';
   139           break;
   140         case 'f':
   141           cbuf[writepos++] = '\f';
   142           break;
   143         case 'n':
   144           cbuf[writepos++] = '\n';
   145           break;
   146         case 'r':
   147           cbuf[writepos++] = '\r';
   148           break;
   149         case 't':
   150           cbuf[writepos++] = '\t';
   151           break;
   152         case 'u':
   153           lua_pushnil(L);
   154           lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet");  // TODO
   155           return 2;
   156         default:
   157           lua_pushnil(L);
   158           lua_pushliteral(L, "Unexpected string escape sequence in JSON document");
   159           return 2;
   160         }
   161       } else {
   162         cbuf[writepos++] = c;
   163       }
   164     }
   165     if (!c) goto json_import_unexpected_eof;
   166     luaL_pushresultsize(&luabuf, writepos);
   167     goto json_import_process_value;
   168   }
   169   if (c == '-' || (c >= '0' && c <= '9')) {
   170     char *endptr;
   171     double numval;
   172     numval = strtod(str+pos, &endptr);
   173     if (endptr == str+pos) goto json_import_syntax_error;
   174     pos += endptr - (str+pos);
   175     lua_pushnumber(L, numval);
   176   } else if (!strncmp(str+pos, "true", 4)) {
   177     lua_pushboolean(L, 1);
   178     pos += 4;
   179   } else if (!strncmp(str+pos, "false", 5)) {
   180     lua_pushboolean(L, 0);
   181     pos += 5;
   182   } else if (!strncmp(str+pos, "null", 4)) {
   183     lua_pushvalue(L, JSON_UPVAL_NULLMARK);
   184     pos += 4;
   185   } else {
   186     goto json_import_syntax_error;
   187   }
   188 json_import_process_value:
   189   switch (mode) {
   190   case JSON_STATE_OBJECT_KEY:
   191     if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error;
   192     mode = JSON_STATE_OBJECT_KEY_TERMINATOR;
   193     goto json_import_loop;
   194   case JSON_STATE_OBJECT_VALUE:
   195     lua_rawset(L, -3);
   196     mode = JSON_STATE_OBJECT_SEPARATOR;
   197     goto json_import_loop;
   198   case JSON_STATE_ARRAY_VALUE:
   199     lua_rawseti(L, -3, lua_rawlen(L, -3) + 1);
   200     mode = JSON_STATE_ARRAY_SEPARATOR;
   201     goto json_import_loop;
   202   case JSON_STATE_VALUE:
   203     mode = JSON_STATE_END;
   204     goto json_import_loop;
   205   }
   206 json_import_syntax_error:
   207   lua_pushnil(L);
   208   lua_pushliteral(L, "Syntax error in JSON document");
   209   return 2;
   210 }
   212 #define JSON_PATH_GET 1
   213 #define JSON_PATH_TYPE 2
   214 #define JSON_PATH_ISNULL 3
   216 static int json_path(lua_State *L, int mode) {
   217   int argc;
   218   int idx = 2;
   219   argc = lua_gettop(L);
   220   lua_pushvalue(L, 1);
   221   while (idx <= argc) {
   222     if (lua_isnil(L, -1)) {
   223       if (mode == JSON_PATH_ISNULL) lua_pushboolean(L, 0);
   224       return 1;
   225     }
   226     lua_pushvalue(L, -1);
   227     lua_rawget(L, JSON_UPVAL_SHADOWTBL);
   228     if (lua_isnil(L, -1)) {
   229       lua_pop(L, 1);
   230       if (lua_type(L, -1) == LUA_TTABLE) {
   231         lua_pushvalue(L, idx++);
   232         lua_gettable(L, -2);
   233       } else {
   234         lua_pushnil(L);
   235       }
   236     } else {
   237       lua_replace(L, -2);
   238       lua_pushvalue(L, idx++);
   239       lua_rawget(L, -2);
   240     }
   241     lua_replace(L, -2);
   242   }
   243   switch (mode) {
   244   case JSON_PATH_GET:
   245     if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) lua_pushnil(L);
   246     return 1;
   247   case JSON_PATH_TYPE:
   248     if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) {
   249       lua_pushliteral(L, "null");
   250       return 1;
   251     }
   252     lua_pushvalue(L, -1);
   253     lua_rawget(L, JSON_UPVAL_TYPES);
   254     if (lua_isnil(L, -1)) lua_pushstring(L, lua_typename(L, lua_type(L, -2)));
   255     return 1;
   256   case JSON_PATH_ISNULL:
   257     lua_pushboolean(L, lua_rawequal(L, -1, JSON_UPVAL_NULLMARK));
   258     return 1;
   259   }
   260   return 0;
   261 }
   263 static int json_get(lua_State *L) {
   264   return json_path(L, JSON_PATH_GET);
   265 }
   267 static int json_type(lua_State *L) {
   268   return json_path(L, JSON_PATH_TYPE);
   269 }
   271 static int json_isnull(lua_State *L) {
   272   return json_path(L, JSON_PATH_ISNULL);
   273 }
   275 static int json_setnull(lua_State *L) {
   276   lua_settop(L, 2);
   277   lua_pushvalue(L, JSON_UPVAL_METATABLE);
   278   lua_setmetatable(L, 1);
   279   lua_pushvalue(L, 1);
   280   lua_rawget(L, JSON_UPVAL_SHADOWTBL);
   281   if (lua_isnil(L, -1)) {
   282     lua_newtable(L);
   283     lua_pushvalue(L, 1);
   284     lua_pushvalue(L, -2);
   285     lua_rawset(L, JSON_UPVAL_SHADOWTBL);
   286   }
   287   lua_pushvalue(L, 2);
   288   lua_pushvalue(L, JSON_UPVAL_NULLMARK);
   289   lua_rawset(L, -3);
   290   return 0;
   291 }
   293 static int json_len(lua_State *L) {
   294   lua_settop(L, 1);
   295   lua_pushvalue(L, 1);
   296   lua_rawget(L, JSON_UPVAL_SHADOWTBL);
   297   if (lua_isnil(L, -1)) lua_pop(L, 1);
   298   lua_pushinteger(L, lua_rawlen(L, -1));
   299   return 1;
   300 }
   302 static int json_index(lua_State *L) {
   303   lua_settop(L, 2);
   304   lua_pushvalue(L, 1);
   305   lua_rawget(L, JSON_UPVAL_SHADOWTBL);
   306   if (lua_isnil(L, -1)) return 1;
   307   lua_pushvalue(L, 2);
   308   lua_rawget(L, -2);
   309   if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) lua_pushnil(L);
   310   return 1;
   311 }
   313 static int json_newindex(lua_State *L) {
   314   lua_settop(L, 3);
   315   lua_pushvalue(L, 1);
   316   lua_rawget(L, JSON_UPVAL_SHADOWTBL);
   317   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
   318   lua_replace(L, 1);
   319   lua_rawset(L, 1);
   320   return 1;
   321 }
   323 static const struct luaL_Reg json_module_functions[] = {
   324   {"import", json_import},
   325   {"get", json_get},
   326   {"type", json_type},
   327   {"isnull", json_isnull},
   328   {"setnull", json_setnull},
   329   {NULL, NULL}
   330 };
   332 static const struct luaL_Reg json_metatable_functions[] = {
   333   {"__len", json_len},
   334   {"__index", json_index},
   335   {"__newindex", json_newindex},
   336   {NULL, NULL}
   337 };
   339 int luaopen_json(lua_State *L) {
   340   lua_settop(L, 0);
   341   lua_newtable(L);  // 1: library table on stack position
   342   lua_newtable(L);  // 2: table used as JSON NULL value in internal shadow tables
   343   lua_newtable(L);  // 3: ephemeron table to store shadow tables for each JSON object/array to allow NULL values returned as nil
   344   lua_newtable(L);  // 4: ephemeron table to store the type of the JSON object/array
   345   lua_newtable(L);  // 5: metatable for ephemeron tables
   346   lua_pushliteral(L, "__mode");
   347   lua_pushliteral(L, "k");
   348   lua_rawset(L, 5);
   349   lua_pushvalue(L, 5);  // 6: cloned metatable reference
   350   lua_setmetatable(L, 3);
   351   lua_setmetatable(L, 4);
   352   lua_newtable(L);  // 5: metatable for JSON objects and JSON arrays
   353   lua_pushvalue(L, 5);
   354   lua_setfield(L, 1, "metatable");
   355   lua_pushvalue(L, 2);
   356   lua_pushvalue(L, 3);
   357   lua_pushvalue(L, 4);
   358   lua_pushvalue(L, 5);
   359   luaL_setfuncs(L, json_metatable_functions, 4);
   360   luaL_setfuncs(L, json_module_functions, 4);
   361   return 1;
   362 }
