jbe@121: #include jbe@121: #include jbe@122: #include jbe@121: #include jbe@121: jbe@130: #define JSON_UPVAL_NULLMARK lua_upvalueindex(1) jbe@130: #define JSON_UPVAL_SHADOWTBL lua_upvalueindex(2) jbe@130: #define JSON_UPVAL_TYPES lua_upvalueindex(3) jbe@130: #define JSON_UPVAL_METATABLE lua_upvalueindex(4) jbe@135: #define JSON_UPVAL_PAIRS_ITERFUNC lua_upvalueindex(5) jbe@135: #define JSON_UPVAL_IPAIRS_ITERFUNC lua_upvalueindex(6) jbe@123: jbe@136: // marks a table as JSON object or JSON array: jbe@136: // (returns its modified argument or a new table if argument is nil) jbe@136: static int json_mark(lua_State *L, const char *marker) { jbe@136: // if argument is nil, then create new table: jbe@136: if (lua_isnoneornil(L, 1)) { jbe@136: lua_settop(L, 0); jbe@136: lua_newtable(L); jbe@136: // skip testing of existing shadow table: jbe@136: goto json_object_create_shadow_table; jbe@136: } jbe@136: lua_pushvalue(L, 1); jbe@136: lua_rawget(L, JSON_UPVAL_SHADOWTBL); jbe@136: if (lua_isnil(L, -1)) { jbe@136: json_object_create_shadow_table: jbe@136: // set shadow table: jbe@136: lua_pushvalue(L, 1); jbe@136: lua_newtable(L); jbe@136: lua_rawset(L, JSON_UPVAL_SHADOWTBL); jbe@136: } jbe@136: // set metatable: jbe@136: lua_pushvalue(L, JSON_UPVAL_METATABLE); jbe@136: lua_setmetatable(L, 1); jbe@136: // mark table as JSON object or as JSON array jbe@136: lua_pushvalue(L, 1); jbe@136: lua_pushstring(L, marker); jbe@136: lua_rawset(L, JSON_UPVAL_TYPES); jbe@136: return 1; jbe@136: } jbe@136: jbe@136: // marks a table as JSON object: jbe@136: // (returns its modified argument or a new table if argument is nil) jbe@136: static int json_object(lua_State *L) { jbe@136: return json_mark(L, "object"); jbe@136: } jbe@136: jbe@136: // marks a table as JSON array: jbe@136: // (returns its modified argument or a new table if argument is nil) jbe@136: static int json_array(lua_State *L) { jbe@136: return json_mark(L, "array"); jbe@136: } jbe@136: jbe@124: #define JSON_STATE_VALUE 0 jbe@124: #define JSON_STATE_OBJECT_KEY 1 jbe@124: #define JSON_STATE_OBJECT_KEY_TERMINATOR 2 jbe@124: #define JSON_STATE_OBJECT_VALUE 3 jbe@124: #define JSON_STATE_OBJECT_SEPARATOR 4 jbe@124: #define JSON_STATE_ARRAY_VALUE 5 jbe@124: #define JSON_STATE_ARRAY_SEPARATOR 6 jbe@124: #define JSON_STATE_END 7 jbe@121: jbe@136: // decodes a JSON document: jbe@121: static int json_import(lua_State *L) { jbe@136: const char *str; // string to parse jbe@136: size_t total; // total length of string to parse jbe@136: size_t pos = 0; // current position in string to parse jbe@136: size_t level = 0; // nested levels of objects/arrays currently being processed jbe@136: int mode = JSON_STATE_VALUE; // state of parser jbe@136: char c; // variable to store a single character to be processed jbe@136: luaL_Buffer luabuf; // Lua buffer to decode (possibly escaped) strings jbe@136: char *cbuf; // C buffer to decode (possibly escaped) strings jbe@136: size_t writepos; // write position of decoded strings in C buffer jbe@136: // require string as first argument: jbe@136: str = luaL_checklstring(L, 1, &total); jbe@136: // if string contains a NULL byte, this is a syntax error jbe@136: if (strlen(str) != total) goto json_import_syntax_error; jbe@136: // main loop of parser: jbe@136: json_import_loop: jbe@136: // skip whitespace and store next character in variable 'c': jbe@121: while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++; jbe@136: // switch statement to handle certain (single) characters: jbe@121: switch (c) { jbe@136: // handle end of JSON document: jbe@121: case 0: jbe@136: // if end of JSON document was expected, then return top element of stack as result: jbe@124: if (mode == JSON_STATE_END) return 1; jbe@136: // otherwise, the JSON document was malformed: jbe@121: json_import_unexpected_eof: jbe@121: lua_pushnil(L); jbe@121: if (level == 0) lua_pushliteral(L, "Empty string"); jbe@121: else lua_pushliteral(L, "Unexpected end of JSON document"); jbe@121: return 2; jbe@136: // new JSON object: jbe@121: case '{': jbe@136: // if a JSON object is not expected here, then return an error: jbe@124: if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE) jbe@121: goto json_import_syntax_error; jbe@136: // consume input character: jbe@121: pos++; jbe@136: // create JSON object on stack: jbe@136: lua_newtable(L); jbe@136: // set metatable of JSON object: jbe@125: lua_pushvalue(L, JSON_UPVAL_METATABLE); jbe@125: lua_setmetatable(L, -2); jbe@136: // mark JSON object as JSON object (as opposed to JSON array): jbe@127: lua_pushvalue(L, -1); jbe@127: lua_pushliteral(L, "object"); jbe@127: lua_rawset(L, JSON_UPVAL_TYPES); jbe@136: // create internal shadow table on stack: jbe@136: lua_newtable(L); jbe@136: // register internal shadow table: jbe@123: lua_pushvalue(L, -2); jbe@123: lua_pushvalue(L, -2); jbe@130: lua_rawset(L, JSON_UPVAL_SHADOWTBL); jbe@136: // increment level: jbe@121: level++; jbe@136: // expect object key (or end of object) and continue with loop: jbe@136: mode = JSON_STATE_OBJECT_KEY; jbe@121: goto json_import_loop; jbe@136: // new JSON array: jbe@121: case '[': jbe@136: // if a JSON array is not expected here, then return an error: jbe@124: if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE) jbe@121: goto json_import_syntax_error; jbe@136: // consume input character: jbe@121: pos++; jbe@136: // create JSON array on stack: jbe@136: lua_newtable(L); jbe@136: // set metatable of JSON array: jbe@125: lua_pushvalue(L, JSON_UPVAL_METATABLE); jbe@125: lua_setmetatable(L, -2); jbe@136: // mark JSON array as JSON array (as opposed to JSON object): jbe@127: lua_pushvalue(L, -1); jbe@127: lua_pushliteral(L, "array"); jbe@127: lua_rawset(L, JSON_UPVAL_TYPES); jbe@136: // create internal shadow table on stack: jbe@136: lua_newtable(L); jbe@136: // register internal shadow table: jbe@123: lua_pushvalue(L, -2); jbe@123: lua_pushvalue(L, -2); jbe@130: lua_rawset(L, JSON_UPVAL_SHADOWTBL); jbe@136: // increment level: jbe@121: level++; jbe@136: // expect array value (or end of array) and continue with loop: jbe@136: mode = JSON_STATE_ARRAY_VALUE; jbe@121: goto json_import_loop; jbe@136: // end of JSON object: jbe@121: case '}': jbe@136: // if end of JSON object is not expected here, then return an error: jbe@124: if (mode != JSON_STATE_OBJECT_KEY && mode != JSON_STATE_OBJECT_SEPARATOR) jbe@121: goto json_import_syntax_error; jbe@136: // jump to common code for end of JSON object and JSON array: jbe@121: goto json_import_close; jbe@136: // end of JSON array: jbe@121: case ']': jbe@136: // if end of JSON array is not expected here, then return an error: jbe@124: if (mode != JSON_STATE_ARRAY_VALUE && mode != JSON_STATE_ARRAY_SEPARATOR) jbe@121: goto json_import_syntax_error; jbe@136: // continue with common code for end of JSON object and JSON array: jbe@136: // common code for end of JSON object or JSON array: jbe@121: json_import_close: jbe@136: // consume input character: jbe@121: pos++; jbe@136: // pop shadow table: jbe@136: lua_pop(L, 1); jbe@136: // check if nested: jbe@121: if (--level) { jbe@136: // if nested, then check if outer(!) structure is an array or object: jbe@136: lua_pushvalue(L, c == '}' ? -4 : -3); jbe@136: lua_rawget(L, JSON_UPVAL_TYPES); jbe@136: if (lua_tostring(L, -1)[0] == 'a') { jbe@136: // select array value processing: jbe@124: mode = JSON_STATE_ARRAY_VALUE; jbe@121: } else { jbe@136: // select object value processing: jbe@124: mode = JSON_STATE_OBJECT_VALUE; jbe@121: } jbe@136: // pop JSON type from stack (from rawget JSON_UPVAL_TYPES): jbe@136: lua_pop(L, 1); jbe@136: // store value in outer structure: jbe@121: goto json_import_process_value; jbe@121: } jbe@136: // if not nested, then expect end of JSON document and continue with loop: jbe@136: mode = JSON_STATE_END; jbe@121: goto json_import_loop; jbe@136: // key terminator: jbe@121: case ':': jbe@136: // if key terminator is not expected here, then return an error: jbe@124: if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR) jbe@121: goto json_import_syntax_error; jbe@136: // consume input character: jbe@121: pos++; jbe@136: // set state of parser and continue with loop: jbe@124: mode = JSON_STATE_OBJECT_VALUE; jbe@121: goto json_import_loop; jbe@136: // value terminator (NOTE: trailing comma at end of value or key-value list is tolerated by this parser) jbe@121: case ',': jbe@136: // change parser state accordingly: jbe@124: if (mode == JSON_STATE_OBJECT_SEPARATOR) { jbe@124: mode = JSON_STATE_OBJECT_KEY; jbe@124: } else if (mode == JSON_STATE_ARRAY_SEPARATOR) { jbe@124: mode = JSON_STATE_ARRAY_VALUE; jbe@121: } else { jbe@136: // if value terminator is not expected here, then return an error: jbe@136: goto json_import_syntax_error; jbe@121: } jbe@136: // consume input character: jbe@121: pos++; jbe@136: // continue with loop: jbe@121: goto json_import_loop; jbe@136: // string literal: jbe@121: case '"': jbe@136: // prepare buffer to decode string (with maximum possible length) and set write position to zero: jbe@121: cbuf = luaL_buffinitsize(L, &luabuf, total-pos); jbe@121: writepos = 0; jbe@136: // consume quote character: jbe@121: pos++; jbe@136: // read next character until encountering end quote: jbe@121: while ((c = str[pos++]) != '"') { jbe@121: if (c == 0) { jbe@136: // handle unexpected end-of-string: jbe@121: goto json_import_unexpected_eof; jbe@121: } else if (c < 32 || c == 127) { jbe@136: // do not allow ASCII control characters: jbe@136: // NOTE: illegal UTF-8 sequences and extended control characters are not sanitized jbe@136: // by this parser to allow different encodings than Unicode jbe@121: lua_pushnil(L); jbe@121: lua_pushliteral(L, "Unexpected control character in JSON string"); jbe@121: return 2; jbe@121: } else if (c == '\\') { jbe@136: // read next char after backslash escape: jbe@121: c = str[pos++]; jbe@121: switch (c) { jbe@136: // unexpected end-of-string: jbe@121: case 0: jbe@121: goto json_import_unexpected_eof; jbe@136: // unescaping of quotation mark, slash, and backslash: jbe@121: case '"': jbe@121: case '/': jbe@121: case '\\': jbe@121: cbuf[writepos++] = c; jbe@121: break; jbe@136: // unescaping of backspace: jbe@121: case 'b': jbe@121: cbuf[writepos++] = '\b'; jbe@121: break; jbe@136: // unescaping of form-feed: jbe@121: case 'f': jbe@121: cbuf[writepos++] = '\f'; jbe@121: break; jbe@136: // unescaping of new-line: jbe@121: case 'n': jbe@121: cbuf[writepos++] = '\n'; jbe@121: break; jbe@136: // unescaping of carriage-return: jbe@121: case 'r': jbe@121: cbuf[writepos++] = '\r'; jbe@121: break; jbe@136: // unescaping of tabulator: jbe@121: case 't': jbe@121: cbuf[writepos++] = '\t'; jbe@121: break; jbe@136: // unescaping of UTF-16 characters jbe@121: case 'u': jbe@121: lua_pushnil(L); jbe@121: lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet"); // TODO jbe@121: return 2; jbe@136: // unexpected escape sequence: jbe@121: default: jbe@121: lua_pushnil(L); jbe@121: lua_pushliteral(L, "Unexpected string escape sequence in JSON document"); jbe@121: return 2; jbe@121: } jbe@121: } else { jbe@136: // normal character: jbe@121: cbuf[writepos++] = c; jbe@121: } jbe@121: } jbe@136: // process buffer to Lua string: jbe@121: luaL_pushresultsize(&luabuf, writepos); jbe@136: // continue with processing of decoded string: jbe@121: goto json_import_process_value; jbe@121: } jbe@136: // process values whose type is is not deducible from a single character: jbe@136: if ((c >= '0' && c <= '9') || c == '-' || c == '+') { jbe@136: // numbers: jbe@122: char *endptr; jbe@122: double numval; jbe@122: numval = strtod(str+pos, &endptr); jbe@122: if (endptr == str+pos) goto json_import_syntax_error; jbe@122: pos += endptr - (str+pos); jbe@122: lua_pushnumber(L, numval); jbe@122: } else if (!strncmp(str+pos, "true", 4)) { jbe@136: // consume 4 input characters for "true": jbe@121: pos += 4; jbe@136: // put Lua true value on stack: jbe@136: lua_pushboolean(L, 1); jbe@121: } else if (!strncmp(str+pos, "false", 5)) { jbe@136: // consume 5 input characters for "false": jbe@121: pos += 5; jbe@136: // put Lua false value on stack: jbe@136: lua_pushboolean(L, 0); jbe@121: } else if (!strncmp(str+pos, "null", 4)) { jbe@136: // consume 4 input characters for "null": jbe@136: pos += 4; jbe@136: // put special null-marker on stack: jbe@130: lua_pushvalue(L, JSON_UPVAL_NULLMARK); jbe@121: } else { jbe@136: // all other cases are a syntax error: jbe@121: goto json_import_syntax_error; jbe@121: } jbe@136: // process a decoded value or key value pair (expected on top of Lua stack): jbe@136: json_import_process_value: jbe@121: switch (mode) { jbe@136: // an object key has been read: jbe@124: case JSON_STATE_OBJECT_KEY: jbe@136: // if an object key is not a string, then this is a syntax error: jbe@121: if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error; jbe@136: // expect key terminator and continue with loop: jbe@124: mode = JSON_STATE_OBJECT_KEY_TERMINATOR; jbe@121: goto json_import_loop; jbe@136: // a key value pair has been read: jbe@124: case JSON_STATE_OBJECT_VALUE: jbe@136: // store key value pair in outer shadow table: jbe@130: lua_rawset(L, -3); jbe@136: // expect value terminator (or end of object) and continue with loop: jbe@124: mode = JSON_STATE_OBJECT_SEPARATOR; jbe@121: goto json_import_loop; jbe@136: // an array value has been read: jbe@124: case JSON_STATE_ARRAY_VALUE: jbe@136: // store value in outer shadow table: jbe@136: lua_rawseti(L, -2, lua_rawlen(L, -2) + 1); jbe@136: // expect value terminator (or end of object) and continue with loop: jbe@124: mode = JSON_STATE_ARRAY_SEPARATOR; jbe@121: goto json_import_loop; jbe@136: // a single value has been read: jbe@124: case JSON_STATE_VALUE: jbe@136: // leave value on top of stack, expect end of JSON document, and continue with loop: jbe@124: mode = JSON_STATE_END; jbe@121: goto json_import_loop; jbe@121: } jbe@136: // syntax error handling (only reachable by goto statement): jbe@136: json_import_syntax_error: jbe@121: lua_pushnil(L); jbe@121: lua_pushliteral(L, "Syntax error in JSON document"); jbe@121: return 2; jbe@121: } jbe@121: jbe@130: #define JSON_PATH_GET 1 jbe@130: #define JSON_PATH_TYPE 2 jbe@130: #define JSON_PATH_ISNULL 3 jbe@130: jbe@136: // gets a value, its type, or information jbe@130: static int json_path(lua_State *L, int mode) { jbe@130: int argc; jbe@130: int idx = 2; jbe@130: argc = lua_gettop(L); jbe@126: lua_pushvalue(L, 1); jbe@130: while (idx <= argc) { jbe@132: if (lua_isnil(L, -1)) { jbe@132: if (mode == JSON_PATH_ISNULL) lua_pushboolean(L, 0); jbe@132: return 1; jbe@132: } jbe@130: lua_pushvalue(L, -1); jbe@130: lua_rawget(L, JSON_UPVAL_SHADOWTBL); jbe@126: if (lua_isnil(L, -1)) { jbe@126: lua_pop(L, 1); jbe@130: if (lua_type(L, -1) == LUA_TTABLE) { jbe@130: lua_pushvalue(L, idx++); jbe@130: lua_gettable(L, -2); jbe@130: } else { jbe@130: lua_pushnil(L); jbe@130: } jbe@130: } else { jbe@130: lua_replace(L, -2); jbe@130: lua_pushvalue(L, idx++); jbe@130: lua_rawget(L, -2); jbe@126: } jbe@130: lua_replace(L, -2); jbe@126: } jbe@130: switch (mode) { jbe@130: case JSON_PATH_GET: jbe@130: if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) lua_pushnil(L); jbe@130: return 1; jbe@130: case JSON_PATH_TYPE: jbe@130: if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) { jbe@130: lua_pushliteral(L, "null"); jbe@130: return 1; jbe@126: } jbe@130: lua_pushvalue(L, -1); jbe@130: lua_rawget(L, JSON_UPVAL_TYPES); jbe@130: if (lua_isnil(L, -1)) lua_pushstring(L, lua_typename(L, lua_type(L, -2))); jbe@130: return 1; jbe@130: case JSON_PATH_ISNULL: jbe@130: lua_pushboolean(L, lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)); jbe@130: return 1; jbe@126: } jbe@130: return 0; jbe@130: } jbe@130: jbe@130: static int json_get(lua_State *L) { jbe@130: return json_path(L, JSON_PATH_GET); jbe@130: } jbe@130: jbe@130: static int json_type(lua_State *L) { jbe@130: return json_path(L, JSON_PATH_TYPE); jbe@130: } jbe@130: jbe@130: static int json_isnull(lua_State *L) { jbe@130: return json_path(L, JSON_PATH_ISNULL); jbe@130: } jbe@130: jbe@131: static int json_setnull(lua_State *L) { jbe@131: lua_settop(L, 2); jbe@131: lua_pushvalue(L, JSON_UPVAL_METATABLE); jbe@131: lua_setmetatable(L, 1); jbe@131: lua_pushvalue(L, 1); jbe@131: lua_rawget(L, JSON_UPVAL_SHADOWTBL); jbe@131: if (lua_isnil(L, -1)) { jbe@131: lua_newtable(L); jbe@131: lua_pushvalue(L, 1); jbe@131: lua_pushvalue(L, -2); jbe@131: lua_rawset(L, JSON_UPVAL_SHADOWTBL); jbe@131: } jbe@131: lua_pushvalue(L, 2); jbe@131: lua_pushvalue(L, JSON_UPVAL_NULLMARK); jbe@131: lua_rawset(L, -3); jbe@131: return 0; jbe@131: } jbe@131: jbe@130: static int json_len(lua_State *L) { jbe@130: lua_settop(L, 1); jbe@130: lua_pushvalue(L, 1); jbe@130: lua_rawget(L, JSON_UPVAL_SHADOWTBL); jbe@130: if (lua_isnil(L, -1)) lua_pop(L, 1); jbe@130: lua_pushinteger(L, lua_rawlen(L, -1)); jbe@123: return 1; jbe@123: } jbe@123: jbe@130: static int json_index(lua_State *L) { jbe@130: lua_settop(L, 2); jbe@130: lua_pushvalue(L, 1); jbe@130: lua_rawget(L, JSON_UPVAL_SHADOWTBL); jbe@130: if (lua_isnil(L, -1)) return 1; jbe@130: lua_pushvalue(L, 2); jbe@130: lua_rawget(L, -2); jbe@130: if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) lua_pushnil(L); jbe@127: return 1; jbe@127: } jbe@127: jbe@130: static int json_newindex(lua_State *L) { jbe@130: lua_settop(L, 3); jbe@123: lua_pushvalue(L, 1); jbe@130: lua_rawget(L, JSON_UPVAL_SHADOWTBL); jbe@130: if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found"); jbe@130: lua_replace(L, 1); jbe@130: lua_rawset(L, 1); jbe@121: return 1; jbe@121: } jbe@121: jbe@135: static int json_pairs_iterfunc(lua_State *L) { jbe@135: lua_settop(L, 2); jbe@135: lua_pushvalue(L, 1); jbe@135: lua_rawget(L, JSON_UPVAL_SHADOWTBL); jbe@135: if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found"); jbe@135: lua_pushvalue(L, 2); jbe@135: if (!lua_next(L, -2)) return 0; jbe@135: if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) { jbe@135: lua_pop(L, 1); jbe@135: lua_pushnil(L); jbe@135: } jbe@135: return 2; jbe@135: } jbe@135: jbe@135: static int json_pairs(lua_State *L) { jbe@135: lua_pushvalue(L, JSON_UPVAL_PAIRS_ITERFUNC); jbe@135: lua_pushvalue(L, 1); jbe@135: lua_pushnil(L); jbe@135: return 3; jbe@135: } jbe@135: jbe@134: static int json_ipairs_iterfunc(lua_State *L) { jbe@134: int idx; jbe@134: lua_settop(L, 2); jbe@134: idx = lua_tointeger(L, 2) + 1; jbe@134: lua_pushvalue(L, 1); jbe@134: lua_rawget(L, JSON_UPVAL_SHADOWTBL); jbe@134: if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found"); jbe@134: lua_rawgeti(L, -1, idx); jbe@134: if (lua_isnil(L, -1)) return 0; jbe@134: lua_pushinteger(L, idx); jbe@134: if (lua_rawequal(L, -2, JSON_UPVAL_NULLMARK)) lua_pushnil(L); jbe@134: else lua_pushvalue(L, -2); jbe@134: return 2; jbe@134: } jbe@134: jbe@134: static int json_ipairs(lua_State *L) { jbe@134: lua_pushvalue(L, JSON_UPVAL_IPAIRS_ITERFUNC); jbe@134: lua_pushvalue(L, 1); jbe@134: lua_pushinteger(L, 0); jbe@134: return 3; jbe@134: } jbe@134: jbe@121: static const struct luaL_Reg json_module_functions[] = { jbe@133: {"object", json_object}, jbe@133: {"array", json_array}, jbe@121: {"import", json_import}, jbe@130: {"get", json_get}, jbe@127: {"type", json_type}, jbe@123: {"isnull", json_isnull}, jbe@131: {"setnull", json_setnull}, jbe@121: {NULL, NULL} jbe@121: }; jbe@121: jbe@126: static const struct luaL_Reg json_metatable_functions[] = { jbe@130: {"__len", json_len}, jbe@130: {"__index", json_index}, jbe@130: {"__newindex", json_newindex}, jbe@135: {"__pairs", json_pairs}, jbe@134: {"__ipairs", json_ipairs}, jbe@126: {NULL, NULL} jbe@126: }; jbe@126: jbe@121: int luaopen_json(lua_State *L) { jbe@126: lua_settop(L, 0); jbe@126: lua_newtable(L); // 1: library table on stack position jbe@130: lua_newtable(L); // 2: table used as JSON NULL value in internal shadow tables jbe@130: lua_newtable(L); // 3: ephemeron table to store shadow tables for each JSON object/array to allow NULL values returned as nil jbe@130: lua_newtable(L); // 4: ephemeron table to store the type of the JSON object/array jbe@130: lua_newtable(L); // 5: metatable for ephemeron tables jbe@121: lua_pushliteral(L, "__mode"); jbe@121: lua_pushliteral(L, "k"); jbe@130: lua_rawset(L, 5); jbe@130: lua_pushvalue(L, 5); // 6: cloned metatable reference jbe@127: lua_setmetatable(L, 3); jbe@130: lua_setmetatable(L, 4); jbe@130: lua_newtable(L); // 5: metatable for JSON objects and JSON arrays jbe@126: lua_pushvalue(L, 2); jbe@126: lua_pushvalue(L, 3); jbe@127: lua_pushvalue(L, 4); jbe@130: lua_pushvalue(L, 5); jbe@135: lua_pushcclosure(L, json_pairs_iterfunc, 4); // 6: iteration function for pairs jbe@135: lua_pushvalue(L, 2); jbe@135: lua_pushvalue(L, 3); jbe@135: lua_pushvalue(L, 4); jbe@135: lua_pushvalue(L, 5); jbe@135: lua_pushcclosure(L, json_ipairs_iterfunc, 4); // 7: iteration function for ipairs jbe@134: lua_pushvalue(L, 5); jbe@134: lua_pushvalue(L, 2); jbe@134: lua_pushvalue(L, 3); jbe@134: lua_pushvalue(L, 4); jbe@134: lua_pushvalue(L, 5); jbe@134: lua_pushvalue(L, 6); jbe@135: lua_pushvalue(L, 7); jbe@135: luaL_setfuncs(L, json_metatable_functions, 6); jbe@135: lua_setfield(L, 1, "metatable"); jbe@135: luaL_setfuncs(L, json_module_functions, 6); jbe@121: return 1; jbe@121: }