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