webmcp
view libraries/json/json.c @ 121:9ad1165cf3a1
Started work on a JSON library
| author | jbe | 
|---|---|
| date | Fri Jul 25 22:25:53 2014 +0200 (2014-07-25) | 
| parents | |
| children | ff39d4a310b9 | 
 line source
     1 #include <lua.h>
     2 #include <lauxlib.h>
     3 #include <string.h>
     5 #define JSON_VALUE 0
     6 #define JSON_OBJECT_KEY 1
     7 #define JSON_OBJECT_KEY_TERMINATOR 2
     8 #define JSON_OBJECT_VALUE 3
     9 #define JSON_OBJECT_SEPARATOR 4
    10 #define JSON_ARRAY_VALUE 5
    11 #define JSON_ARRAY_SEPARATOR 6
    12 #define JSON_END 7
    14 static int json_import(lua_State *L) {
    15   const char *str;
    16   size_t total;
    17   size_t pos = 0;
    18   size_t level = 0;
    19   int mode = JSON_VALUE;
    20   char c;
    21   luaL_Buffer luabuf;
    22   char *cbuf;
    23   size_t writepos;
    24   int aryidx;
    25   lua_settop(L, 1);
    26   str = lua_tostring(L, 1);
    27   total = strlen(str);
    28 json_import_loop:
    29   while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++;
    30   switch (c) {
    31   case 0:
    32     if (mode == JSON_END) return 1;
    33     json_import_unexpected_eof:
    34     lua_pushnil(L);
    35     if (level == 0) lua_pushliteral(L, "Empty string");
    36     else lua_pushliteral(L, "Unexpected end of JSON document");
    37     return 2;
    38   case '{':
    39     if (mode != JSON_VALUE && mode != JSON_OBJECT_VALUE && mode != JSON_ARRAY_VALUE)
    40       goto json_import_syntax_error;
    41     pos++;
    42     lua_newtable(L);
    43     mode = JSON_OBJECT_KEY;
    44     level++;
    45     goto json_import_loop;
    46   case '[':
    47     if (mode != JSON_VALUE && mode != JSON_OBJECT_VALUE && mode != JSON_ARRAY_VALUE)
    48       goto json_import_syntax_error;
    49     pos++;
    50     lua_newtable(L);
    51     lua_pushinteger(L, 0);  // length of array (since it may contain nil's)
    52     mode = JSON_ARRAY_VALUE;
    53     level++;
    54     goto json_import_loop;
    55   case '}':
    56     if (mode != JSON_OBJECT_KEY && mode != JSON_OBJECT_SEPARATOR)
    57       goto json_import_syntax_error;
    58     goto json_import_close;
    59   case ']':
    60     if (mode != JSON_ARRAY_VALUE && mode != JSON_ARRAY_SEPARATOR)
    61       goto json_import_syntax_error;
    62     lua_pushvalue(L, -2);  // use array table as key
    63     lua_insert(L, -2);  // use length of array as value
    64     lua_rawset(L, lua_upvalueindex(1));  // store length in ephemeron table
    65     // leaves array table on top of stack
    66   json_import_close:
    67     pos++;
    68     if (--level) {
    69       if (lua_type(L, -2) == LUA_TNUMBER) {
    70         mode = JSON_ARRAY_VALUE;
    71       } else {
    72         mode = JSON_OBJECT_VALUE;
    73       }
    74       goto json_import_process_value;
    75     } else {
    76       mode = JSON_END;
    77     }
    78     goto json_import_loop;
    79   case ':':
    80     if (mode != JSON_OBJECT_KEY_TERMINATOR)
    81       goto json_import_syntax_error;
    82     pos++;
    83     mode = JSON_OBJECT_VALUE;
    84     goto json_import_loop;
    85   case ',':
    86     if (mode == JSON_OBJECT_SEPARATOR) {
    87       mode = JSON_OBJECT_KEY;
    88     } else if (mode == JSON_ARRAY_SEPARATOR) {
    89       mode = JSON_ARRAY_VALUE;
    90     } else {
    91       goto json_import_syntax_error;
    92     }
    93     pos++;
    94     goto json_import_loop;
    95   case '"':
    96     cbuf = luaL_buffinitsize(L, &luabuf, total-pos);
    97     writepos = 0;
    98     pos++;
    99     while ((c = str[pos++]) != '"') {
   100       if (c == 0) {
   101         goto json_import_unexpected_eof;
   102       } else if (c < 32 || c == 127) {
   103         lua_pushnil(L);
   104         lua_pushliteral(L, "Unexpected control character in JSON string");
   105         return 2;
   106       } else if (c == '\\') {
   107         c = str[pos++];
   108         switch (c) {
   109         case 0:
   110           goto json_import_unexpected_eof;
   111         case '"':
   112         case '/':
   113         case '\\':
   114           cbuf[writepos++] = c;
   115           break;
   116         case 'b':
   117           cbuf[writepos++] = '\b';
   118           break;
   119         case 'f':
   120           cbuf[writepos++] = '\f';
   121           break;
   122         case 'n':
   123           cbuf[writepos++] = '\n';
   124           break;
   125         case 'r':
   126           cbuf[writepos++] = '\r';
   127           break;
   128         case 't':
   129           cbuf[writepos++] = '\t';
   130           break;
   131         case 'u':
   132           lua_pushnil(L);
   133           lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet");  // TODO
   134           return 2;
   135         default:
   136           lua_pushnil(L);
   137           lua_pushliteral(L, "Unexpected string escape sequence in JSON document");
   138           return 2;
   139         }
   140       } else {
   141         cbuf[writepos++] = c;
   142       }
   143     }
   144     if (!c) goto json_import_unexpected_eof;
   145     luaL_pushresultsize(&luabuf, writepos);
   146     goto json_import_process_value;
   147   }
   148   if (!strncmp(str+pos, "true", 4)) {
   149     lua_pushboolean(L, 1);
   150     pos += 4;
   151   } else if (!strncmp(str+pos, "false", 5)) {
   152     lua_pushboolean(L, 0);
   153     pos += 5;
   154   } else if (!strncmp(str+pos, "null", 4)) {
   155     lua_pushnil(L);
   156     pos += 4;
   157   } else {
   158     goto json_import_syntax_error;
   159   }
   160 json_import_process_value:
   161   switch (mode) {
   162   case JSON_OBJECT_KEY:
   163     if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error;
   164     mode = JSON_OBJECT_KEY_TERMINATOR;
   165     goto json_import_loop;
   166   case JSON_OBJECT_VALUE:
   167     lua_rawset(L, -3);
   168     mode = JSON_OBJECT_SEPARATOR;
   169     goto json_import_loop;
   170   case JSON_ARRAY_VALUE:
   171     aryidx = lua_tointeger(L, -2) + 1;
   172     lua_rawseti(L, -3, aryidx);
   173     lua_pop(L, 1);
   174     lua_pushinteger(L, aryidx);
   175     mode = JSON_ARRAY_SEPARATOR;
   176     goto json_import_loop;
   177   case JSON_VALUE:
   178     mode = JSON_END;
   179     goto json_import_loop;
   180   }
   181 json_import_syntax_error:
   182   lua_pushnil(L);
   183   lua_pushliteral(L, "Syntax error in JSON document");
   184   return 2;
   185 }
   187 static int json_arylen(lua_State *L) {
   188   lua_settop(L, 1);
   189   lua_rawget(L, lua_upvalueindex(1));
   190   return 1;
   191 }
   193 static const struct luaL_Reg json_module_functions[] = {
   194   {"import", json_import},
   195   {"arylen", json_arylen},
   196   {NULL, NULL}
   197 };
   199 int luaopen_json(lua_State *L) {
   200   lua_newtable(L);  // library table
   201   lua_newtable(L);  // ephemeron table to store the length of arrays (that may contain nil's)
   202   lua_newtable(L);  // meta table for ephemeron table
   203   lua_pushliteral(L, "__mode");
   204   lua_pushliteral(L, "k");
   205   lua_rawset(L, -3);
   206   lua_setmetatable(L, -2);
   207   luaL_setfuncs(L, json_module_functions, 1);
   208   return 1;
   209 }
