webmcp

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

Impressum / About Us