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