webmcp

annotate libraries/json/json.c @ 144:0690fe79b673

Improved macros for handling Lua registry in JSON library
author jbe
date Wed Jul 30 19:24:06 2014 +0200 (2014-07-30)
parents 6f723e60acdc
children 0edc2f05c66a
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@144 6 // maximum number of nested JSON values (objects and arrays):
jbe@142 7 #define JSON_MAXDEPTH 100
jbe@142 8
jbe@144 9 // macros for usage of Lua registry:
jbe@144 10 #define JSON_REGENT char
jbe@138 11 #define JSON_REGREF void *
jbe@144 12 #define json_regref(x) (&json_registry.x)
jbe@144 13 #define json_regfetchref(L, x) (lua_pushlightuserdata((L), (x)), lua_rawget((L), LUA_REGISTRYINDEX))
jbe@144 14 #define json_regfetch(L, x) (json_regfetchref(L, json_regref(x)))
jbe@144 15 #define json_regstore(L, x) (lua_pushlightuserdata(L, json_regref(x)), lua_pushvalue(L, -2), lua_rawset(L, LUA_REGISTRYINDEX));
jbe@138 16
jbe@144 17 // generate dummy memory addresses that represent Lua objects
jbe@144 18 // (via lightuserdata keys and LUA_REGISTRYINDEX):
jbe@144 19 static struct {
jbe@144 20 JSON_REGENT nullmark;
jbe@144 21 JSON_REGENT shadowtbl;
jbe@144 22 JSON_REGENT unknownmt;
jbe@144 23 JSON_REGENT objectmt;
jbe@144 24 JSON_REGENT arraymt;
jbe@144 25 } json_registry;
jbe@138 26
jbe@136 27 // marks a table as JSON object or JSON array:
jbe@136 28 // (returns its modified argument or a new table if argument is nil)
jbe@138 29 static int json_mark(lua_State *L, JSON_REGREF mt) {
jbe@136 30 // if argument is nil, then create new table:
jbe@136 31 if (lua_isnoneornil(L, 1)) {
jbe@136 32 lua_settop(L, 0);
jbe@136 33 lua_newtable(L);
jbe@136 34 // set shadow table:
jbe@144 35 json_regfetch(L, shadowtbl);
jbe@136 36 lua_pushvalue(L, 1);
jbe@136 37 lua_newtable(L);
jbe@143 38 lua_rawset(L, -3);
jbe@143 39 } else {
jbe@143 40 // check if shadow table already exists:
jbe@144 41 json_regfetch(L, shadowtbl);
jbe@143 42 lua_pushvalue(L, 1);
jbe@143 43 lua_rawget(L, -2);
jbe@143 44 if (lua_isnil(L, -1)) {
jbe@143 45 // set shadow table and leave it on top of stack:
jbe@143 46 lua_newtable(L);
jbe@143 47 lua_pushvalue(L, 1);
jbe@143 48 lua_pushvalue(L, -2);
jbe@143 49 lua_rawset(L, -5);
jbe@143 50 }
jbe@143 51 for(lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) {
jbe@143 52 lua_pushvalue(L, -2);
jbe@143 53 lua_pushnil(L);
jbe@143 54 lua_rawset(L, 1);
jbe@143 55 lua_pushvalue(L, -2);
jbe@143 56 lua_pushvalue(L, -2);
jbe@143 57 lua_rawset(L, -5);
jbe@143 58 }
jbe@136 59 }
jbe@138 60 // discard everything but table to return:
jbe@138 61 lua_settop(L, 1);
jbe@136 62 // set metatable:
jbe@144 63 json_regfetchref(L, mt);
jbe@136 64 lua_setmetatable(L, 1);
jbe@138 65 // return table:
jbe@136 66 return 1;
jbe@136 67 }
jbe@136 68
jbe@136 69 // marks a table as JSON object:
jbe@136 70 // (returns its modified argument or a new table if argument is nil)
jbe@136 71 static int json_object(lua_State *L) {
jbe@144 72 return json_mark(L, json_regref(objectmt));
jbe@136 73 }
jbe@136 74
jbe@136 75 // marks a table as JSON array:
jbe@136 76 // (returns its modified argument or a new table if argument is nil)
jbe@136 77 static int json_array(lua_State *L) {
jbe@144 78 return json_mark(L, json_regref(arraymt));
jbe@136 79 }
jbe@136 80
jbe@124 81 #define JSON_STATE_VALUE 0
jbe@124 82 #define JSON_STATE_OBJECT_KEY 1
jbe@124 83 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2
jbe@124 84 #define JSON_STATE_OBJECT_VALUE 3
jbe@124 85 #define JSON_STATE_OBJECT_SEPARATOR 4
jbe@124 86 #define JSON_STATE_ARRAY_VALUE 5
jbe@124 87 #define JSON_STATE_ARRAY_SEPARATOR 6
jbe@124 88 #define JSON_STATE_END 7
jbe@121 89
jbe@138 90 #define json_import_objectmt_idx 2
jbe@138 91 #define json_import_arraymt_idx 3
jbe@138 92 #define json_import_shadowtbl_idx 4
jbe@138 93 #define json_import_nullmark_idx 5
jbe@138 94
jbe@136 95 // decodes a JSON document:
jbe@121 96 static int json_import(lua_State *L) {
jbe@136 97 const char *str; // string to parse
jbe@136 98 size_t total; // total length of string to parse
jbe@136 99 size_t pos = 0; // current position in string to parse
jbe@136 100 size_t level = 0; // nested levels of objects/arrays currently being processed
jbe@136 101 int mode = JSON_STATE_VALUE; // state of parser
jbe@136 102 char c; // variable to store a single character to be processed
jbe@136 103 luaL_Buffer luabuf; // Lua buffer to decode (possibly escaped) strings
jbe@136 104 char *cbuf; // C buffer to decode (possibly escaped) strings
jbe@136 105 size_t writepos; // write position of decoded strings in C buffer
jbe@138 106 // limit stack to 1 element:
jbe@138 107 lua_settop(L, 1);
jbe@138 108 // push json_objectmt on stack position 2:
jbe@144 109 json_regfetch(L, objectmt);
jbe@138 110 // push json_arraymt on stack position 3:
jbe@144 111 json_regfetch(L, arraymt);
jbe@138 112 // push json_shadowtbl on stack position 4:
jbe@144 113 json_regfetch(L, shadowtbl);
jbe@138 114 // push json_nullmark on stack position 5:
jbe@144 115 json_regfetch(L, nullmark);
jbe@136 116 // require string as first argument:
jbe@136 117 str = luaL_checklstring(L, 1, &total);
jbe@136 118 // if string contains a NULL byte, this is a syntax error
jbe@136 119 if (strlen(str) != total) goto json_import_syntax_error;
jbe@136 120 // main loop of parser:
jbe@136 121 json_import_loop:
jbe@136 122 // skip whitespace and store next character in variable 'c':
jbe@121 123 while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++;
jbe@136 124 // switch statement to handle certain (single) characters:
jbe@121 125 switch (c) {
jbe@136 126 // handle end of JSON document:
jbe@121 127 case 0:
jbe@136 128 // if end of JSON document was expected, then return top element of stack as result:
jbe@124 129 if (mode == JSON_STATE_END) return 1;
jbe@136 130 // otherwise, the JSON document was malformed:
jbe@121 131 json_import_unexpected_eof:
jbe@121 132 lua_pushnil(L);
jbe@121 133 if (level == 0) lua_pushliteral(L, "Empty string");
jbe@121 134 else lua_pushliteral(L, "Unexpected end of JSON document");
jbe@121 135 return 2;
jbe@136 136 // new JSON object:
jbe@121 137 case '{':
jbe@136 138 // if a JSON object is not expected here, then return an error:
jbe@124 139 if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE)
jbe@121 140 goto json_import_syntax_error;
jbe@136 141 // create JSON object on stack:
jbe@136 142 lua_newtable(L);
jbe@136 143 // set metatable of JSON object:
jbe@138 144 lua_pushvalue(L, json_import_objectmt_idx);
jbe@125 145 lua_setmetatable(L, -2);
jbe@136 146 // create internal shadow table on stack:
jbe@136 147 lua_newtable(L);
jbe@138 148 // register internal shadow table (and cleanup stack afterwards):
jbe@123 149 lua_pushvalue(L, -2);
jbe@123 150 lua_pushvalue(L, -2);
jbe@138 151 lua_rawset(L, json_import_shadowtbl_idx);
jbe@136 152 // expect object key (or end of object) and continue with loop:
jbe@136 153 mode = JSON_STATE_OBJECT_KEY;
jbe@142 154 goto json_import_open;
jbe@136 155 // new JSON array:
jbe@121 156 case '[':
jbe@136 157 // if a JSON array is not expected here, then return an error:
jbe@124 158 if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE)
jbe@121 159 goto json_import_syntax_error;
jbe@136 160 // create JSON array on stack:
jbe@136 161 lua_newtable(L);
jbe@136 162 // set metatable of JSON array:
jbe@138 163 lua_pushvalue(L, json_import_arraymt_idx);
jbe@125 164 lua_setmetatable(L, -2);
jbe@136 165 // create internal shadow table on stack:
jbe@136 166 lua_newtable(L);
jbe@138 167 // register internal shadow table (and cleanup stack afterwards):
jbe@123 168 lua_pushvalue(L, -2);
jbe@123 169 lua_pushvalue(L, -2);
jbe@138 170 lua_rawset(L, json_import_shadowtbl_idx);
jbe@140 171 // add nil as key (needed to keep stack balance) and as magic to detect arrays:
jbe@140 172 lua_pushnil(L);
jbe@142 173 // expect array value (or end of array) and continue with loop:
jbe@142 174 mode = JSON_STATE_ARRAY_VALUE;
jbe@142 175 // continue with common code for opening JSON object and JSON array:
jbe@142 176 // commn code for opening JSON object or JSON array:
jbe@142 177 json_import_open:
jbe@142 178 // limit nested levels:
jbe@142 179 if (level >= JSON_MAXDEPTH) {
jbe@142 180 lua_pushnil(L);
jbe@142 181 lua_pushliteral(L, "Too many nested JSON levels");
jbe@142 182 return 2;
jbe@142 183 }
jbe@142 184 // additional buffer overflow protection:
jbe@142 185 if (!lua_checkstack(L, LUA_MINSTACK))
jbe@142 186 return luaL_error(L, "Caught stack overflow in JSON import function (too many nested levels and stack size too small)");
jbe@136 187 // increment level:
jbe@121 188 level++;
jbe@142 189 // consume input character:
jbe@142 190 pos++;
jbe@121 191 goto json_import_loop;
jbe@136 192 // end of JSON object:
jbe@121 193 case '}':
jbe@136 194 // if end of JSON object is not expected here, then return an error:
jbe@124 195 if (mode != JSON_STATE_OBJECT_KEY && mode != JSON_STATE_OBJECT_SEPARATOR)
jbe@121 196 goto json_import_syntax_error;
jbe@136 197 // jump to common code for end of JSON object and JSON array:
jbe@121 198 goto json_import_close;
jbe@136 199 // end of JSON array:
jbe@121 200 case ']':
jbe@136 201 // if end of JSON array is not expected here, then return an error:
jbe@124 202 if (mode != JSON_STATE_ARRAY_VALUE && mode != JSON_STATE_ARRAY_SEPARATOR)
jbe@121 203 goto json_import_syntax_error;
jbe@140 204 // pop nil key/magic:
jbe@140 205 lua_pop(L, 1);
jbe@136 206 // continue with common code for end of JSON object and JSON array:
jbe@136 207 // common code for end of JSON object or JSON array:
jbe@121 208 json_import_close:
jbe@136 209 // consume input character:
jbe@121 210 pos++;
jbe@136 211 // pop shadow table:
jbe@136 212 lua_pop(L, 1);
jbe@136 213 // check if nested:
jbe@121 214 if (--level) {
jbe@136 215 // if nested, then check if outer(!) structure is an array or object:
jbe@140 216 if (lua_isnil(L, -2)) {
jbe@136 217 // select array value processing:
jbe@124 218 mode = JSON_STATE_ARRAY_VALUE;
jbe@121 219 } else {
jbe@136 220 // select object value processing:
jbe@124 221 mode = JSON_STATE_OBJECT_VALUE;
jbe@121 222 }
jbe@136 223 // store value in outer structure:
jbe@121 224 goto json_import_process_value;
jbe@121 225 }
jbe@136 226 // if not nested, then expect end of JSON document and continue with loop:
jbe@136 227 mode = JSON_STATE_END;
jbe@121 228 goto json_import_loop;
jbe@136 229 // key terminator:
jbe@121 230 case ':':
jbe@136 231 // if key terminator is not expected here, then return an error:
jbe@124 232 if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR)
jbe@121 233 goto json_import_syntax_error;
jbe@136 234 // consume input character:
jbe@121 235 pos++;
jbe@136 236 // set state of parser and continue with loop:
jbe@124 237 mode = JSON_STATE_OBJECT_VALUE;
jbe@121 238 goto json_import_loop;
jbe@136 239 // value terminator (NOTE: trailing comma at end of value or key-value list is tolerated by this parser)
jbe@121 240 case ',':
jbe@136 241 // change parser state accordingly:
jbe@124 242 if (mode == JSON_STATE_OBJECT_SEPARATOR) {
jbe@124 243 mode = JSON_STATE_OBJECT_KEY;
jbe@124 244 } else if (mode == JSON_STATE_ARRAY_SEPARATOR) {
jbe@124 245 mode = JSON_STATE_ARRAY_VALUE;
jbe@121 246 } else {
jbe@136 247 // if value terminator is not expected here, then return an error:
jbe@136 248 goto json_import_syntax_error;
jbe@121 249 }
jbe@136 250 // consume input character:
jbe@121 251 pos++;
jbe@136 252 // continue with loop:
jbe@121 253 goto json_import_loop;
jbe@136 254 // string literal:
jbe@121 255 case '"':
jbe@136 256 // prepare buffer to decode string (with maximum possible length) and set write position to zero:
jbe@121 257 cbuf = luaL_buffinitsize(L, &luabuf, total-pos);
jbe@121 258 writepos = 0;
jbe@136 259 // consume quote character:
jbe@121 260 pos++;
jbe@136 261 // read next character until encountering end quote:
jbe@121 262 while ((c = str[pos++]) != '"') {
jbe@121 263 if (c == 0) {
jbe@136 264 // handle unexpected end-of-string:
jbe@121 265 goto json_import_unexpected_eof;
jbe@121 266 } else if (c < 32 || c == 127) {
jbe@136 267 // do not allow ASCII control characters:
jbe@136 268 // NOTE: illegal UTF-8 sequences and extended control characters are not sanitized
jbe@136 269 // by this parser to allow different encodings than Unicode
jbe@121 270 lua_pushnil(L);
jbe@121 271 lua_pushliteral(L, "Unexpected control character in JSON string");
jbe@121 272 return 2;
jbe@121 273 } else if (c == '\\') {
jbe@136 274 // read next char after backslash escape:
jbe@121 275 c = str[pos++];
jbe@121 276 switch (c) {
jbe@136 277 // unexpected end-of-string:
jbe@121 278 case 0:
jbe@121 279 goto json_import_unexpected_eof;
jbe@136 280 // unescaping of quotation mark, slash, and backslash:
jbe@121 281 case '"':
jbe@121 282 case '/':
jbe@121 283 case '\\':
jbe@121 284 cbuf[writepos++] = c;
jbe@121 285 break;
jbe@136 286 // unescaping of backspace:
jbe@121 287 case 'b':
jbe@121 288 cbuf[writepos++] = '\b';
jbe@121 289 break;
jbe@136 290 // unescaping of form-feed:
jbe@121 291 case 'f':
jbe@121 292 cbuf[writepos++] = '\f';
jbe@121 293 break;
jbe@136 294 // unescaping of new-line:
jbe@121 295 case 'n':
jbe@121 296 cbuf[writepos++] = '\n';
jbe@121 297 break;
jbe@136 298 // unescaping of carriage-return:
jbe@121 299 case 'r':
jbe@121 300 cbuf[writepos++] = '\r';
jbe@121 301 break;
jbe@136 302 // unescaping of tabulator:
jbe@121 303 case 't':
jbe@121 304 cbuf[writepos++] = '\t';
jbe@121 305 break;
jbe@136 306 // unescaping of UTF-16 characters
jbe@121 307 case 'u':
jbe@121 308 lua_pushnil(L);
jbe@121 309 lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet"); // TODO
jbe@121 310 return 2;
jbe@136 311 // unexpected escape sequence:
jbe@121 312 default:
jbe@121 313 lua_pushnil(L);
jbe@121 314 lua_pushliteral(L, "Unexpected string escape sequence in JSON document");
jbe@121 315 return 2;
jbe@121 316 }
jbe@121 317 } else {
jbe@136 318 // normal character:
jbe@121 319 cbuf[writepos++] = c;
jbe@121 320 }
jbe@121 321 }
jbe@136 322 // process buffer to Lua string:
jbe@121 323 luaL_pushresultsize(&luabuf, writepos);
jbe@136 324 // continue with processing of decoded string:
jbe@121 325 goto json_import_process_value;
jbe@121 326 }
jbe@136 327 // process values whose type is is not deducible from a single character:
jbe@136 328 if ((c >= '0' && c <= '9') || c == '-' || c == '+') {
jbe@136 329 // numbers:
jbe@122 330 char *endptr;
jbe@122 331 double numval;
jbe@122 332 numval = strtod(str+pos, &endptr);
jbe@122 333 if (endptr == str+pos) goto json_import_syntax_error;
jbe@122 334 pos += endptr - (str+pos);
jbe@122 335 lua_pushnumber(L, numval);
jbe@122 336 } else if (!strncmp(str+pos, "true", 4)) {
jbe@136 337 // consume 4 input characters for "true":
jbe@121 338 pos += 4;
jbe@136 339 // put Lua true value on stack:
jbe@136 340 lua_pushboolean(L, 1);
jbe@121 341 } else if (!strncmp(str+pos, "false", 5)) {
jbe@136 342 // consume 5 input characters for "false":
jbe@121 343 pos += 5;
jbe@136 344 // put Lua false value on stack:
jbe@136 345 lua_pushboolean(L, 0);
jbe@121 346 } else if (!strncmp(str+pos, "null", 4)) {
jbe@136 347 // consume 4 input characters for "null":
jbe@136 348 pos += 4;
jbe@136 349 // put special null-marker on stack:
jbe@138 350 lua_pushvalue(L, json_import_nullmark_idx);
jbe@121 351 } else {
jbe@136 352 // all other cases are a syntax error:
jbe@121 353 goto json_import_syntax_error;
jbe@121 354 }
jbe@136 355 // process a decoded value or key value pair (expected on top of Lua stack):
jbe@136 356 json_import_process_value:
jbe@121 357 switch (mode) {
jbe@136 358 // an object key has been read:
jbe@124 359 case JSON_STATE_OBJECT_KEY:
jbe@136 360 // if an object key is not a string, then this is a syntax error:
jbe@121 361 if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error;
jbe@136 362 // expect key terminator and continue with loop:
jbe@124 363 mode = JSON_STATE_OBJECT_KEY_TERMINATOR;
jbe@121 364 goto json_import_loop;
jbe@136 365 // a key value pair has been read:
jbe@124 366 case JSON_STATE_OBJECT_VALUE:
jbe@136 367 // store key value pair in outer shadow table:
jbe@130 368 lua_rawset(L, -3);
jbe@136 369 // expect value terminator (or end of object) and continue with loop:
jbe@124 370 mode = JSON_STATE_OBJECT_SEPARATOR;
jbe@121 371 goto json_import_loop;
jbe@136 372 // an array value has been read:
jbe@124 373 case JSON_STATE_ARRAY_VALUE:
jbe@136 374 // store value in outer shadow table:
jbe@140 375 lua_rawseti(L, -3, lua_rawlen(L, -3) + 1);
jbe@136 376 // expect value terminator (or end of object) and continue with loop:
jbe@124 377 mode = JSON_STATE_ARRAY_SEPARATOR;
jbe@121 378 goto json_import_loop;
jbe@136 379 // a single value has been read:
jbe@124 380 case JSON_STATE_VALUE:
jbe@136 381 // leave value on top of stack, expect end of JSON document, and continue with loop:
jbe@124 382 mode = JSON_STATE_END;
jbe@121 383 goto json_import_loop;
jbe@121 384 }
jbe@136 385 // syntax error handling (only reachable by goto statement):
jbe@136 386 json_import_syntax_error:
jbe@121 387 lua_pushnil(L);
jbe@121 388 lua_pushliteral(L, "Syntax error in JSON document");
jbe@121 389 return 2;
jbe@121 390 }
jbe@121 391
jbe@138 392 #define json_path_shadowtbl_idx 1
jbe@138 393 #define json_path_nullmark_idx 2
jbe@138 394 #define json_path_idxshift 2
jbe@138 395
jbe@137 396 // gets a value or its type from a JSON document (first argument)
jbe@137 397 // optionally using a path (variable number of keys after first argument):
jbe@137 398 static int json_path(lua_State *L, int type_mode) {
jbe@138 399 int stacktop;
jbe@138 400 int idx = 2 + json_path_idxshift;
jbe@138 401 // insert json_shadowtbl on stack at position 1:
jbe@144 402 json_regfetch(L, shadowtbl);
jbe@138 403 lua_insert(L, 1);
jbe@138 404 // insert json_nullmark on stack at position 2:
jbe@144 405 json_regfetch(L, nullmark);
jbe@138 406 lua_insert(L, 2);
jbe@137 407 // store number of arguments:
jbe@138 408 stacktop = lua_gettop(L);
jbe@137 409 // follow path, starting with first argument as "current value":
jbe@138 410 lua_pushvalue(L, 1 + json_path_idxshift);
jbe@137 411 // process each "path key":
jbe@138 412 while (idx <= stacktop) {
jbe@137 413 // if "current value" is nil, then the path cannot be walked and nil is returned:
jbe@137 414 if (lua_isnil(L, -1)) return 1;
jbe@137 415 // try to get shadow table of "current value":
jbe@130 416 lua_pushvalue(L, -1);
jbe@138 417 lua_rawget(L, json_path_shadowtbl_idx);
jbe@126 418 if (lua_isnil(L, -1)) {
jbe@137 419 // if no shadow table is found,
jbe@137 420 // drop nil from stack:
jbe@126 421 lua_pop(L, 1);
jbe@130 422 if (lua_type(L, -1) == LUA_TTABLE) {
jbe@137 423 // if "current value" is a table,
jbe@137 424 // get "next value" using the "path key":
jbe@130 425 lua_pushvalue(L, idx++);
jbe@130 426 lua_gettable(L, -2);
jbe@130 427 } else {
jbe@137 428 // if "current value" is not a table,
jbe@137 429 // then the path cannot be walked and nil is returned:
jbe@130 430 lua_pushnil(L);
jbe@137 431 return 1;
jbe@130 432 }
jbe@130 433 } else {
jbe@137 434 // if a shadow table is found,
jbe@137 435 // set "current value" to its shadow table:
jbe@130 436 lua_replace(L, -2);
jbe@137 437 // get "next value" using the "path key":
jbe@130 438 lua_pushvalue(L, idx++);
jbe@130 439 lua_rawget(L, -2);
jbe@126 440 }
jbe@137 441 // the "next value" replaces the "current value":
jbe@130 442 lua_replace(L, -2);
jbe@126 443 }
jbe@137 444 if (!type_mode) {
jbe@137 445 // if a value (and not its type) was requested,
jbe@137 446 // check if value is the null-marker, and store nil on top of Lua stack in that case:
jbe@138 447 if (lua_rawequal(L, -1, json_path_nullmark_idx)) lua_pushnil(L);
jbe@137 448 } else {
jbe@137 449 // if the type was requested,
jbe@137 450 // check if value is the null-marker:
jbe@138 451 if (lua_rawequal(L, -1, json_path_nullmark_idx)) {
jbe@137 452 // if yes, store string "null" on top of Lua stack:
jbe@130 453 lua_pushliteral(L, "null");
jbe@137 454 } else {
jbe@137 455 // otherwise,
jbe@138 456 // check if metatable indicates "object" or "array":
jbe@138 457 if (lua_getmetatable(L, -1)) {
jbe@144 458 json_regfetch(L, objectmt);
jbe@138 459 if (lua_rawequal(L, -2, -1)) {
jbe@138 460 // return string "object":
jbe@138 461 lua_pushliteral(L, "object");
jbe@138 462 return 1;
jbe@138 463 }
jbe@144 464 json_regfetch(L, arraymt);
jbe@138 465 if (lua_rawequal(L, -3, -1)) {
jbe@138 466 // return string "array":
jbe@138 467 lua_pushliteral(L, "array");
jbe@138 468 return 1;
jbe@138 469 }
jbe@138 470 lua_pop(L, 3);
jbe@138 471 }
jbe@138 472 // otherwise, get the Lua type:
jbe@138 473 lua_pushstring(L, lua_typename(L, lua_type(L, -1)));
jbe@126 474 }
jbe@126 475 }
jbe@137 476 // return the top most value on the Lua stack:
jbe@137 477 return 1;
jbe@130 478 }
jbe@130 479
jbe@137 480 // gets a value from a JSON document (first argument)
jbe@137 481 // optionally using a path (variable number of keys after first argument):
jbe@130 482 static int json_get(lua_State *L) {
jbe@137 483 return json_path(L, 0);
jbe@130 484 }
jbe@130 485
jbe@137 486 // gets a value's type from a JSON document (first argument)
jbe@137 487 // optionally using a path (variable number of keys after first argument):
jbe@130 488 static int json_type(lua_State *L) {
jbe@137 489 return json_path(L, 1);
jbe@130 490 }
jbe@130 491
jbe@137 492 // checks if a value in a JSON document (first argument) is null:
jbe@130 493 static int json_isnull(lua_State *L) {
jbe@137 494 const char *jsontype;
jbe@138 495 lua_pushcfunction(L, json_type);
jbe@137 496 lua_insert(L, 1);
jbe@137 497 lua_call(L, lua_gettop(L) - 1, 1);
jbe@137 498 jsontype = lua_tostring(L, -1);
jbe@137 499 if (jsontype && !strcmp(jsontype, "null")) lua_pushboolean(L, 1);
jbe@137 500 else lua_pushboolean(L, 0);
jbe@137 501 return 1;
jbe@130 502 }
jbe@130 503
jbe@138 504 #define json_setnull_unknownmt_idx 3
jbe@138 505 #define json_setnull_objectmt_idx 4
jbe@138 506 #define json_setnull_arraymt_idx 5
jbe@138 507 #define json_setnull_shadowtbl_idx 6
jbe@138 508
jbe@131 509 static int json_setnull(lua_State *L) {
jbe@138 510 // truncate stack to two elements:
jbe@131 511 lua_settop(L, 2);
jbe@138 512 // push json_unknownmt to stack position 3:
jbe@144 513 json_regfetch(L, unknownmt);
jbe@138 514 // push json_objectmt to stack position 4:
jbe@144 515 json_regfetch(L, objectmt);
jbe@138 516 // push json_arraymt to stack position 5:
jbe@144 517 json_regfetch(L, arraymt);
jbe@138 518 // push json_shadowtbl to stack position 6:
jbe@144 519 json_regfetch(L, shadowtbl);
jbe@138 520 //
jbe@138 521 lua_getmetatable(L, 1);
jbe@138 522 if (
jbe@138 523 !lua_rawequal(L, -1, json_setnull_unknownmt_idx) &&
jbe@138 524 !lua_rawequal(L, -1, json_setnull_objectmt_idx) &&
jbe@138 525 !lua_rawequal(L, -1, json_setnull_arraymt_idx)
jbe@138 526 ) {
jbe@138 527 lua_pushvalue(L, json_setnull_unknownmt_idx);
jbe@138 528 lua_setmetatable(L, 1);
jbe@138 529 }
jbe@131 530 lua_pushvalue(L, 1);
jbe@138 531 lua_rawget(L, json_setnull_shadowtbl_idx);
jbe@131 532 if (lua_isnil(L, -1)) {
jbe@131 533 lua_newtable(L);
jbe@131 534 lua_pushvalue(L, 1);
jbe@131 535 lua_pushvalue(L, -2);
jbe@138 536 lua_rawset(L, json_setnull_shadowtbl_idx);
jbe@131 537 }
jbe@131 538 lua_pushvalue(L, 2);
jbe@144 539 json_regfetch(L, nullmark);
jbe@131 540 lua_rawset(L, -3);
jbe@131 541 return 0;
jbe@131 542 }
jbe@131 543
jbe@130 544 static int json_len(lua_State *L) {
jbe@130 545 lua_settop(L, 1);
jbe@144 546 json_regfetch(L, shadowtbl);
jbe@130 547 lua_pushvalue(L, 1);
jbe@138 548 lua_rawget(L, -2);
jbe@138 549 lua_pushinteger(L, lua_rawlen(L, lua_isnil(L, -1) ? 1 : -1));
jbe@123 550 return 1;
jbe@123 551 }
jbe@123 552
jbe@141 553 #define json_index_nullmark_idx 3
jbe@141 554 #define json_index_shadowtbl_idx 4
jbe@141 555
jbe@130 556 static int json_index(lua_State *L) {
jbe@130 557 lua_settop(L, 2);
jbe@144 558 json_regfetch(L, nullmark); // on stack position 3
jbe@144 559 json_regfetch(L, shadowtbl);
jbe@130 560 lua_pushvalue(L, 1);
jbe@141 561 lua_rawget(L, json_index_shadowtbl_idx);
jbe@139 562 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
jbe@130 563 lua_pushvalue(L, 2);
jbe@130 564 lua_rawget(L, -2);
jbe@141 565 if (lua_rawequal(L, -1, json_index_nullmark_idx)) lua_pushnil(L);
jbe@127 566 return 1;
jbe@127 567 }
jbe@127 568
jbe@130 569 static int json_newindex(lua_State *L) {
jbe@130 570 lua_settop(L, 3);
jbe@144 571 json_regfetch(L, shadowtbl);
jbe@123 572 lua_pushvalue(L, 1);
jbe@143 573 lua_rawget(L, -2);
jbe@130 574 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
jbe@130 575 lua_replace(L, 1);
jbe@139 576 lua_settop(L, 3);
jbe@130 577 lua_rawset(L, 1);
jbe@121 578 return 1;
jbe@121 579 }
jbe@121 580
jbe@139 581 #define json_pairs_iterfunc_nullmark_idx 3
jbe@139 582 #define json_pairs_iterfunc_shadowtbl_idx 4
jbe@139 583
jbe@135 584 static int json_pairs_iterfunc(lua_State *L) {
jbe@135 585 lua_settop(L, 2);
jbe@144 586 json_regfetch(L, nullmark); // on stack position 3
jbe@144 587 json_regfetch(L, shadowtbl);
jbe@135 588 lua_pushvalue(L, 1);
jbe@139 589 lua_rawget(L, json_pairs_iterfunc_shadowtbl_idx);
jbe@135 590 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
jbe@135 591 lua_pushvalue(L, 2);
jbe@135 592 if (!lua_next(L, -2)) return 0;
jbe@139 593 if (lua_rawequal(L, -1, json_pairs_iterfunc_nullmark_idx)) {
jbe@135 594 lua_pop(L, 1);
jbe@135 595 lua_pushnil(L);
jbe@135 596 }
jbe@135 597 return 2;
jbe@135 598 }
jbe@135 599
jbe@135 600 static int json_pairs(lua_State *L) {
jbe@139 601 lua_pushcfunction(L, json_pairs_iterfunc);
jbe@135 602 lua_pushvalue(L, 1);
jbe@135 603 lua_pushnil(L);
jbe@135 604 return 3;
jbe@135 605 }
jbe@135 606
jbe@139 607 #define json_ipairs_iterfunc_nullmark_idx 3
jbe@139 608 #define json_ipairs_iterfunc_shadowtbl_idx 4
jbe@139 609
jbe@134 610 static int json_ipairs_iterfunc(lua_State *L) {
jbe@134 611 int idx;
jbe@134 612 lua_settop(L, 2);
jbe@144 613 json_regfetch(L, nullmark); // on stack position 3
jbe@144 614 json_regfetch(L, shadowtbl);
jbe@134 615 idx = lua_tointeger(L, 2) + 1;
jbe@134 616 lua_pushvalue(L, 1);
jbe@139 617 lua_rawget(L, json_ipairs_iterfunc_shadowtbl_idx);
jbe@134 618 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
jbe@134 619 lua_rawgeti(L, -1, idx);
jbe@134 620 if (lua_isnil(L, -1)) return 0;
jbe@134 621 lua_pushinteger(L, idx);
jbe@139 622 if (lua_rawequal(L, -2, json_ipairs_iterfunc_nullmark_idx)) lua_pushnil(L);
jbe@134 623 else lua_pushvalue(L, -2);
jbe@134 624 return 2;
jbe@134 625 }
jbe@134 626
jbe@134 627 static int json_ipairs(lua_State *L) {
jbe@139 628 lua_pushcfunction(L, json_ipairs_iterfunc);
jbe@134 629 lua_pushvalue(L, 1);
jbe@134 630 lua_pushinteger(L, 0);
jbe@134 631 return 3;
jbe@134 632 }
jbe@134 633
jbe@121 634 static const struct luaL_Reg json_module_functions[] = {
jbe@133 635 {"object", json_object},
jbe@133 636 {"array", json_array},
jbe@121 637 {"import", json_import},
jbe@130 638 {"get", json_get},
jbe@127 639 {"type", json_type},
jbe@123 640 {"isnull", json_isnull},
jbe@131 641 {"setnull", json_setnull},
jbe@121 642 {NULL, NULL}
jbe@121 643 };
jbe@121 644
jbe@126 645 static const struct luaL_Reg json_metatable_functions[] = {
jbe@130 646 {"__len", json_len},
jbe@130 647 {"__index", json_index},
jbe@130 648 {"__newindex", json_newindex},
jbe@135 649 {"__pairs", json_pairs},
jbe@134 650 {"__ipairs", json_ipairs},
jbe@126 651 {NULL, NULL}
jbe@126 652 };
jbe@126 653
jbe@121 654 int luaopen_json(lua_State *L) {
jbe@126 655 lua_settop(L, 0);
jbe@138 656 lua_newtable(L); // library
jbe@138 657 lua_newtable(L);
jbe@138 658 luaL_setfuncs(L, json_metatable_functions, 0);
jbe@144 659 json_regstore(L, unknownmt);
jbe@138 660 lua_setfield(L, 1, "ambiguous_mt");
jbe@138 661 lua_newtable(L);
jbe@138 662 luaL_setfuncs(L, json_metatable_functions, 0);
jbe@144 663 json_regstore(L, objectmt);
jbe@138 664 lua_setfield(L, 1, "object_mt");
jbe@138 665 lua_newtable(L);
jbe@138 666 luaL_setfuncs(L, json_metatable_functions, 0);
jbe@144 667 json_regstore(L, arraymt);
jbe@138 668 lua_setfield(L, 1, "array_mt");
jbe@138 669 lua_newtable(L); // ephemeron table to store shadow tables for each JSON object/array to allow NULL values returned as nil
jbe@138 670 lua_newtable(L); // metatable for ephemeron table
jbe@121 671 lua_pushliteral(L, "__mode");
jbe@121 672 lua_pushliteral(L, "k");
jbe@138 673 lua_rawset(L, -3);
jbe@138 674 lua_setmetatable(L, -2);
jbe@144 675 json_regstore(L, shadowtbl);
jbe@138 676 lua_newtable(L);
jbe@144 677 json_regstore(L, nullmark);
jbe@138 678 lua_settop(L, 1);
jbe@138 679 luaL_setfuncs(L, json_module_functions, 0);
jbe@121 680 return 1;
jbe@121 681 }

Impressum / About Us