| rev | 
   line source | 
| 
jbe@121
 | 
     1 #include <lua.h>
 | 
| 
jbe@121
 | 
     2 #include <lauxlib.h>
 | 
| 
jbe@122
 | 
     3 #include <stdlib.h>
 | 
| 
jbe@121
 | 
     4 #include <string.h>
 | 
| 
jbe@121
 | 
     5 
 | 
| 
jbe@125
 | 
     6 #define JSON_UPVAL_METATABLE lua_upvalueindex(1)
 | 
| 
jbe@123
 | 
     7 #define JSON_UPVAL_NULLS lua_upvalueindex(2)
 | 
| 
jbe@125
 | 
     8 #define JSON_UPVAL_ARYLEN lua_upvalueindex(3)
 | 
| 
jbe@123
 | 
     9 
 | 
| 
jbe@124
 | 
    10 #define JSON_STATE_VALUE 0
 | 
| 
jbe@124
 | 
    11 #define JSON_STATE_OBJECT_KEY 1
 | 
| 
jbe@124
 | 
    12 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2
 | 
| 
jbe@124
 | 
    13 #define JSON_STATE_OBJECT_VALUE 3
 | 
| 
jbe@124
 | 
    14 #define JSON_STATE_OBJECT_SEPARATOR 4
 | 
| 
jbe@124
 | 
    15 #define JSON_STATE_ARRAY_VALUE 5
 | 
| 
jbe@124
 | 
    16 #define JSON_STATE_ARRAY_SEPARATOR 6
 | 
| 
jbe@124
 | 
    17 #define JSON_STATE_END 7
 | 
| 
jbe@121
 | 
    18 
 | 
| 
jbe@121
 | 
    19 static int json_import(lua_State *L) {
 | 
| 
jbe@121
 | 
    20   const char *str;
 | 
| 
jbe@121
 | 
    21   size_t total;
 | 
| 
jbe@121
 | 
    22   size_t pos = 0;
 | 
| 
jbe@121
 | 
    23   size_t level = 0;
 | 
| 
jbe@124
 | 
    24   int mode = JSON_STATE_VALUE;
 | 
| 
jbe@121
 | 
    25   char c;
 | 
| 
jbe@121
 | 
    26   luaL_Buffer luabuf;
 | 
| 
jbe@121
 | 
    27   char *cbuf;
 | 
| 
jbe@121
 | 
    28   size_t writepos;
 | 
| 
jbe@121
 | 
    29   int aryidx;
 | 
| 
jbe@121
 | 
    30   lua_settop(L, 1);
 | 
| 
jbe@121
 | 
    31   str = lua_tostring(L, 1);
 | 
| 
jbe@121
 | 
    32   total = strlen(str);
 | 
| 
jbe@121
 | 
    33 json_import_loop:
 | 
| 
jbe@121
 | 
    34   while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++;
 | 
| 
jbe@121
 | 
    35   switch (c) {
 | 
| 
jbe@121
 | 
    36   case 0:
 | 
| 
jbe@124
 | 
    37     if (mode == JSON_STATE_END) return 1;
 | 
| 
jbe@121
 | 
    38     json_import_unexpected_eof:
 | 
| 
jbe@121
 | 
    39     lua_pushnil(L);
 | 
| 
jbe@121
 | 
    40     if (level == 0) lua_pushliteral(L, "Empty string");
 | 
| 
jbe@121
 | 
    41     else lua_pushliteral(L, "Unexpected end of JSON document");
 | 
| 
jbe@121
 | 
    42     return 2;
 | 
| 
jbe@121
 | 
    43   case '{':
 | 
| 
jbe@124
 | 
    44     if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE)
 | 
| 
jbe@121
 | 
    45       goto json_import_syntax_error;
 | 
| 
jbe@121
 | 
    46     pos++;
 | 
| 
jbe@125
 | 
    47     lua_newtable(L);  // the actual JSON object
 | 
| 
jbe@125
 | 
    48     lua_pushvalue(L, JSON_UPVAL_METATABLE);
 | 
| 
jbe@125
 | 
    49     lua_setmetatable(L, -2);
 | 
| 
jbe@123
 | 
    50     lua_newtable(L);  // stores set of NULL values
 | 
| 
jbe@123
 | 
    51     lua_pushvalue(L, -2);
 | 
| 
jbe@123
 | 
    52     lua_pushvalue(L, -2);
 | 
| 
jbe@123
 | 
    53     lua_rawset(L, JSON_UPVAL_NULLS);
 | 
| 
jbe@124
 | 
    54     mode = JSON_STATE_OBJECT_KEY;
 | 
| 
jbe@121
 | 
    55     level++;
 | 
| 
jbe@121
 | 
    56     goto json_import_loop;
 | 
| 
jbe@121
 | 
    57   case '[':
 | 
| 
jbe@124
 | 
    58     if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE)
 | 
| 
jbe@121
 | 
    59       goto json_import_syntax_error;
 | 
| 
jbe@121
 | 
    60     pos++;
 | 
| 
jbe@125
 | 
    61     lua_newtable(L);  // the actual JSON array
 | 
| 
jbe@125
 | 
    62     lua_pushvalue(L, JSON_UPVAL_METATABLE);
 | 
| 
jbe@125
 | 
    63     lua_setmetatable(L, -2);
 | 
| 
jbe@123
 | 
    64     lua_newtable(L);  // stores set of NULL values
 | 
| 
jbe@123
 | 
    65     lua_pushvalue(L, -2);
 | 
| 
jbe@123
 | 
    66     lua_pushvalue(L, -2);
 | 
| 
jbe@123
 | 
    67     lua_rawset(L, JSON_UPVAL_NULLS);
 | 
| 
jbe@121
 | 
    68     lua_pushinteger(L, 0);  // length of array (since it may contain nil's)
 | 
| 
jbe@124
 | 
    69     mode = JSON_STATE_ARRAY_VALUE;
 | 
| 
jbe@121
 | 
    70     level++;
 | 
| 
jbe@121
 | 
    71     goto json_import_loop;
 | 
| 
jbe@121
 | 
    72   case '}':
 | 
| 
jbe@124
 | 
    73     if (mode != JSON_STATE_OBJECT_KEY && mode != JSON_STATE_OBJECT_SEPARATOR)
 | 
| 
jbe@121
 | 
    74       goto json_import_syntax_error;
 | 
| 
jbe@121
 | 
    75     goto json_import_close;
 | 
| 
jbe@121
 | 
    76   case ']':
 | 
| 
jbe@124
 | 
    77     if (mode != JSON_STATE_ARRAY_VALUE && mode != JSON_STATE_ARRAY_SEPARATOR)
 | 
| 
jbe@121
 | 
    78       goto json_import_syntax_error;
 | 
| 
jbe@121
 | 
    79     lua_pushvalue(L, -2);  // use array table as key
 | 
| 
jbe@121
 | 
    80     lua_insert(L, -2);  // use length of array as value
 | 
| 
jbe@123
 | 
    81     lua_rawset(L, JSON_UPVAL_ARYLEN);  // store length in ephemeron table
 | 
| 
jbe@121
 | 
    82     // leaves array table on top of stack
 | 
| 
jbe@121
 | 
    83   json_import_close:
 | 
| 
jbe@121
 | 
    84     pos++;
 | 
| 
jbe@123
 | 
    85     lua_pop(L, 1);  // pop table that stores set of NULL values
 | 
| 
jbe@121
 | 
    86     if (--level) {
 | 
| 
jbe@121
 | 
    87       if (lua_type(L, -2) == LUA_TNUMBER) {
 | 
| 
jbe@124
 | 
    88         mode = JSON_STATE_ARRAY_VALUE;
 | 
| 
jbe@121
 | 
    89       } else {
 | 
| 
jbe@124
 | 
    90         mode = JSON_STATE_OBJECT_VALUE;
 | 
| 
jbe@121
 | 
    91       }
 | 
| 
jbe@121
 | 
    92       goto json_import_process_value;
 | 
| 
jbe@121
 | 
    93     } else {
 | 
| 
jbe@124
 | 
    94       mode = JSON_STATE_END;
 | 
| 
jbe@121
 | 
    95     }
 | 
| 
jbe@121
 | 
    96     goto json_import_loop;
 | 
| 
jbe@121
 | 
    97   case ':':
 | 
| 
jbe@124
 | 
    98     if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR)
 | 
| 
jbe@121
 | 
    99       goto json_import_syntax_error;
 | 
| 
jbe@121
 | 
   100     pos++;
 | 
| 
jbe@124
 | 
   101     mode = JSON_STATE_OBJECT_VALUE;
 | 
| 
jbe@121
 | 
   102     goto json_import_loop;
 | 
| 
jbe@121
 | 
   103   case ',':
 | 
| 
jbe@124
 | 
   104     if (mode == JSON_STATE_OBJECT_SEPARATOR) {
 | 
| 
jbe@124
 | 
   105       mode = JSON_STATE_OBJECT_KEY;
 | 
| 
jbe@124
 | 
   106     } else if (mode == JSON_STATE_ARRAY_SEPARATOR) {
 | 
| 
jbe@124
 | 
   107       mode = JSON_STATE_ARRAY_VALUE;
 | 
| 
jbe@121
 | 
   108     } else {
 | 
| 
jbe@121
 | 
   109       goto json_import_syntax_error;
 | 
| 
jbe@121
 | 
   110     }
 | 
| 
jbe@121
 | 
   111     pos++;
 | 
| 
jbe@121
 | 
   112     goto json_import_loop;
 | 
| 
jbe@121
 | 
   113   case '"':
 | 
| 
jbe@121
 | 
   114     cbuf = luaL_buffinitsize(L, &luabuf, total-pos);
 | 
| 
jbe@121
 | 
   115     writepos = 0;
 | 
| 
jbe@121
 | 
   116     pos++;
 | 
| 
jbe@121
 | 
   117     while ((c = str[pos++]) != '"') {
 | 
| 
jbe@121
 | 
   118       if (c == 0) {
 | 
| 
jbe@121
 | 
   119         goto json_import_unexpected_eof;
 | 
| 
jbe@121
 | 
   120       } else if (c < 32 || c == 127) {
 | 
| 
jbe@121
 | 
   121         lua_pushnil(L);
 | 
| 
jbe@121
 | 
   122         lua_pushliteral(L, "Unexpected control character in JSON string");
 | 
| 
jbe@121
 | 
   123         return 2;
 | 
| 
jbe@121
 | 
   124       } else if (c == '\\') {
 | 
| 
jbe@121
 | 
   125         c = str[pos++];
 | 
| 
jbe@121
 | 
   126         switch (c) {
 | 
| 
jbe@121
 | 
   127         case 0:
 | 
| 
jbe@121
 | 
   128           goto json_import_unexpected_eof;
 | 
| 
jbe@121
 | 
   129         case '"':
 | 
| 
jbe@121
 | 
   130         case '/':
 | 
| 
jbe@121
 | 
   131         case '\\':
 | 
| 
jbe@121
 | 
   132           cbuf[writepos++] = c;
 | 
| 
jbe@121
 | 
   133           break;
 | 
| 
jbe@121
 | 
   134         case 'b':
 | 
| 
jbe@121
 | 
   135           cbuf[writepos++] = '\b';
 | 
| 
jbe@121
 | 
   136           break;
 | 
| 
jbe@121
 | 
   137         case 'f':
 | 
| 
jbe@121
 | 
   138           cbuf[writepos++] = '\f';
 | 
| 
jbe@121
 | 
   139           break;
 | 
| 
jbe@121
 | 
   140         case 'n':
 | 
| 
jbe@121
 | 
   141           cbuf[writepos++] = '\n';
 | 
| 
jbe@121
 | 
   142           break;
 | 
| 
jbe@121
 | 
   143         case 'r':
 | 
| 
jbe@121
 | 
   144           cbuf[writepos++] = '\r';
 | 
| 
jbe@121
 | 
   145           break;
 | 
| 
jbe@121
 | 
   146         case 't':
 | 
| 
jbe@121
 | 
   147           cbuf[writepos++] = '\t';
 | 
| 
jbe@121
 | 
   148           break;
 | 
| 
jbe@121
 | 
   149         case 'u':
 | 
| 
jbe@121
 | 
   150           lua_pushnil(L);
 | 
| 
jbe@121
 | 
   151           lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet");  // TODO
 | 
| 
jbe@121
 | 
   152           return 2;
 | 
| 
jbe@121
 | 
   153         default:
 | 
| 
jbe@121
 | 
   154           lua_pushnil(L);
 | 
| 
jbe@121
 | 
   155           lua_pushliteral(L, "Unexpected string escape sequence in JSON document");
 | 
| 
jbe@121
 | 
   156           return 2;
 | 
| 
jbe@121
 | 
   157         }
 | 
| 
jbe@121
 | 
   158       } else {
 | 
| 
jbe@121
 | 
   159         cbuf[writepos++] = c;
 | 
| 
jbe@121
 | 
   160       }
 | 
| 
jbe@121
 | 
   161     }
 | 
| 
jbe@121
 | 
   162     if (!c) goto json_import_unexpected_eof;
 | 
| 
jbe@121
 | 
   163     luaL_pushresultsize(&luabuf, writepos);
 | 
| 
jbe@121
 | 
   164     goto json_import_process_value;
 | 
| 
jbe@121
 | 
   165   }
 | 
| 
jbe@122
 | 
   166   if (c == '-' || (c >= '0' && c <= '9')) {
 | 
| 
jbe@122
 | 
   167     char *endptr;
 | 
| 
jbe@122
 | 
   168     double numval;
 | 
| 
jbe@122
 | 
   169     numval = strtod(str+pos, &endptr);
 | 
| 
jbe@122
 | 
   170     if (endptr == str+pos) goto json_import_syntax_error;
 | 
| 
jbe@122
 | 
   171     pos += endptr - (str+pos);
 | 
| 
jbe@122
 | 
   172     lua_pushnumber(L, numval);
 | 
| 
jbe@122
 | 
   173   } else if (!strncmp(str+pos, "true", 4)) {
 | 
| 
jbe@121
 | 
   174     lua_pushboolean(L, 1);
 | 
| 
jbe@121
 | 
   175     pos += 4;
 | 
| 
jbe@121
 | 
   176   } else if (!strncmp(str+pos, "false", 5)) {
 | 
| 
jbe@121
 | 
   177     lua_pushboolean(L, 0);
 | 
| 
jbe@121
 | 
   178     pos += 5;
 | 
| 
jbe@121
 | 
   179   } else if (!strncmp(str+pos, "null", 4)) {
 | 
| 
jbe@121
 | 
   180     lua_pushnil(L);
 | 
| 
jbe@121
 | 
   181     pos += 4;
 | 
| 
jbe@121
 | 
   182   } else {
 | 
| 
jbe@121
 | 
   183     goto json_import_syntax_error;
 | 
| 
jbe@121
 | 
   184   }
 | 
| 
jbe@121
 | 
   185 json_import_process_value:
 | 
| 
jbe@121
 | 
   186   switch (mode) {
 | 
| 
jbe@124
 | 
   187   case JSON_STATE_OBJECT_KEY:
 | 
| 
jbe@121
 | 
   188     if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error;
 | 
| 
jbe@124
 | 
   189     mode = JSON_STATE_OBJECT_KEY_TERMINATOR;
 | 
| 
jbe@121
 | 
   190     goto json_import_loop;
 | 
| 
jbe@124
 | 
   191   case JSON_STATE_OBJECT_VALUE:
 | 
| 
jbe@123
 | 
   192     if (lua_isnil(L, -1)) {
 | 
| 
jbe@123
 | 
   193       lua_pushvalue(L, -2);
 | 
| 
jbe@123
 | 
   194       lua_pushboolean(L, 1);
 | 
| 
jbe@123
 | 
   195       lua_rawset(L, -5);
 | 
| 
jbe@123
 | 
   196     }
 | 
| 
jbe@123
 | 
   197     lua_rawset(L, -4);
 | 
| 
jbe@124
 | 
   198     mode = JSON_STATE_OBJECT_SEPARATOR;
 | 
| 
jbe@121
 | 
   199     goto json_import_loop;
 | 
| 
jbe@124
 | 
   200   case JSON_STATE_ARRAY_VALUE:
 | 
| 
jbe@121
 | 
   201     aryidx = lua_tointeger(L, -2) + 1;
 | 
| 
jbe@123
 | 
   202     if (lua_isnil(L, -1)) {
 | 
| 
jbe@123
 | 
   203       lua_pushinteger(L, aryidx);
 | 
| 
jbe@123
 | 
   204       lua_pushboolean(L, 1);
 | 
| 
jbe@123
 | 
   205       lua_rawset(L, -5);
 | 
| 
jbe@123
 | 
   206     }
 | 
| 
jbe@123
 | 
   207     lua_rawseti(L, -4, aryidx);
 | 
| 
jbe@121
 | 
   208     lua_pop(L, 1);
 | 
| 
jbe@121
 | 
   209     lua_pushinteger(L, aryidx);
 | 
| 
jbe@124
 | 
   210     mode = JSON_STATE_ARRAY_SEPARATOR;
 | 
| 
jbe@121
 | 
   211     goto json_import_loop;
 | 
| 
jbe@124
 | 
   212   case JSON_STATE_VALUE:
 | 
| 
jbe@124
 | 
   213     mode = JSON_STATE_END;
 | 
| 
jbe@121
 | 
   214     goto json_import_loop;
 | 
| 
jbe@121
 | 
   215   }
 | 
| 
jbe@121
 | 
   216 json_import_syntax_error:
 | 
| 
jbe@121
 | 
   217   lua_pushnil(L);
 | 
| 
jbe@121
 | 
   218   lua_pushliteral(L, "Syntax error in JSON document");
 | 
| 
jbe@121
 | 
   219   return 2;
 | 
| 
jbe@121
 | 
   220 }
 | 
| 
jbe@121
 | 
   221 
 | 
| 
jbe@121
 | 
   222 static int json_arylen(lua_State *L) {
 | 
| 
jbe@121
 | 
   223   lua_settop(L, 1);
 | 
| 
jbe@123
 | 
   224   lua_rawget(L, JSON_UPVAL_ARYLEN);
 | 
| 
jbe@123
 | 
   225   return 1;
 | 
| 
jbe@123
 | 
   226 }
 | 
| 
jbe@123
 | 
   227 
 | 
| 
jbe@123
 | 
   228 static int json_isnull(lua_State *L) {
 | 
| 
jbe@123
 | 
   229   lua_pushvalue(L, 1);
 | 
| 
jbe@123
 | 
   230   lua_rawget(L, JSON_UPVAL_NULLS);
 | 
| 
jbe@123
 | 
   231   if (lua_isnil(L, -1)) goto json_isnull_false;
 | 
| 
jbe@123
 | 
   232   lua_pushvalue(L, 2);
 | 
| 
jbe@123
 | 
   233   lua_rawget(L, -2);
 | 
| 
jbe@123
 | 
   234   if (!lua_isnil(L, -1)) return 1;
 | 
| 
jbe@123
 | 
   235 json_isnull_false:
 | 
| 
jbe@123
 | 
   236   lua_pushboolean(L, 0);
 | 
| 
jbe@121
 | 
   237   return 1;
 | 
| 
jbe@121
 | 
   238 }
 | 
| 
jbe@121
 | 
   239 
 | 
| 
jbe@121
 | 
   240 static const struct luaL_Reg json_module_functions[] = {
 | 
| 
jbe@121
 | 
   241   {"import", json_import},
 | 
| 
jbe@121
 | 
   242   {"arylen", json_arylen},
 | 
| 
jbe@123
 | 
   243   {"isnull", json_isnull},
 | 
| 
jbe@121
 | 
   244   {NULL, NULL}
 | 
| 
jbe@121
 | 
   245 };
 | 
| 
jbe@121
 | 
   246 
 | 
| 
jbe@121
 | 
   247 int luaopen_json(lua_State *L) {
 | 
| 
jbe@121
 | 
   248   lua_newtable(L);  // library table
 | 
| 
jbe@125
 | 
   249   lua_newtable(L);  // metatable for JSON objects and JSON arrays
 | 
| 
jbe@125
 | 
   250   lua_pushvalue(L, -1);
 | 
| 
jbe@125
 | 
   251   lua_setfield(L, -3, "metatable");
 | 
| 
jbe@125
 | 
   252   lua_newtable(L);  // ephemeron table to store a set of keys associated with JSON-null values
 | 
| 
jbe@121
 | 
   253   lua_newtable(L);  // ephemeron table to store the length of arrays (that may contain nil's)
 | 
| 
jbe@121
 | 
   254   lua_newtable(L);  // meta table for ephemeron table
 | 
| 
jbe@121
 | 
   255   lua_pushliteral(L, "__mode");
 | 
| 
jbe@121
 | 
   256   lua_pushliteral(L, "k");
 | 
| 
jbe@121
 | 
   257   lua_rawset(L, -3);
 | 
| 
jbe@123
 | 
   258   lua_pushvalue(L, -1);
 | 
| 
jbe@123
 | 
   259   lua_setmetatable(L, -3);
 | 
| 
jbe@121
 | 
   260   lua_setmetatable(L, -2);
 | 
| 
jbe@125
 | 
   261   luaL_setfuncs(L, json_module_functions, 3);
 | 
| 
jbe@121
 | 
   262   return 1;
 | 
| 
jbe@121
 | 
   263 }
 |