webmcp

annotate libraries/json/json.c @ 134:a1507b499fa5

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

Impressum / About Us