# HG changeset patch # User jbe # Date 1406746482 -7200 # Node ID 7912a05ebc3f4d874acc43381902123add114c37 # Parent 0edc2f05c66ab52201c0f15a800e84770337a82c Moved lua_pop(L, 1) call to conditional expression and added documentation in JSON library diff -r 0edc2f05c66a -r 7912a05ebc3f libraries/json/json.c --- a/libraries/json/json.c Wed Jul 30 19:59:11 2014 +0200 +++ b/libraries/json/json.c Wed Jul 30 20:54:42 2014 +0200 @@ -15,10 +15,9 @@ #define json_regfetch(L, x) (json_regfetchpointer(L, json_regpointer(x))) #define json_regstore(L, x) (lua_pushlightuserdata(L, json_regpointer(x)), lua_pushvalue(L, -2), lua_rawset(L, LUA_REGISTRYINDEX)); -// generate dummy memory addresses that represent Lua objects -// directly via lightuserdata values (not using the Lua registry): +// generate dummy memory addresses that represent non-modifiable lightuserdata (dummy) objects: static struct { - JSON_REGENT nullmark; // magic value to indicate JSON null value + JSON_REGENT nullmark; // magic value to indicate JSON null value in shadow table } json_reference; @@ -132,7 +131,14 @@ // main loop of parser: json_import_loop: // skip whitespace and store next character in variable 'c': - while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++; + while (c = str[pos], + c == ' ' || + c == '\f' || + c == '\n' || + c == '\r' || + c == '\t' || + c == '\v' + ) pos++; // switch statement to handle certain (single) characters: switch (c) { // handle end of JSON document: @@ -148,8 +154,11 @@ // new JSON object: case '{': // if a JSON object is not expected here, then return an error: - if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE) - goto json_import_syntax_error; + if ( + mode != JSON_STATE_VALUE && + mode != JSON_STATE_OBJECT_VALUE && + mode != JSON_STATE_ARRAY_VALUE + ) goto json_import_syntax_error; // create JSON object on stack: lua_newtable(L); // set metatable of JSON object: @@ -157,18 +166,22 @@ lua_setmetatable(L, -2); // create internal shadow table on stack: lua_newtable(L); - // register internal shadow table (and cleanup stack afterwards): + // register internal shadow table: lua_pushvalue(L, -2); lua_pushvalue(L, -2); lua_rawset(L, json_import_shadowtbl_idx); - // expect object key (or end of object) and continue with loop: + // expect object key (or end of object) to follow: mode = JSON_STATE_OBJECT_KEY; + // jump to common code for opening JSON object and JSON array: goto json_import_open; // new JSON array: case '[': // if a JSON array is not expected here, then return an error: - if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE) - goto json_import_syntax_error; + if ( + mode != JSON_STATE_VALUE && + mode != JSON_STATE_OBJECT_VALUE && + mode != JSON_STATE_ARRAY_VALUE + ) goto json_import_syntax_error; // create JSON array on stack: lua_newtable(L); // set metatable of JSON array: @@ -176,16 +189,16 @@ lua_setmetatable(L, -2); // create internal shadow table on stack: lua_newtable(L); - // register internal shadow table (and cleanup stack afterwards): + // register internal shadow table: lua_pushvalue(L, -2); lua_pushvalue(L, -2); lua_rawset(L, json_import_shadowtbl_idx); // add nil as key (needed to keep stack balance) and as magic to detect arrays: lua_pushnil(L); - // expect array value (or end of array) and continue with loop: + // expect array value (or end of array) to follow: mode = JSON_STATE_ARRAY_VALUE; // continue with common code for opening JSON object and JSON array: - // commn code for opening JSON object or JSON array: + // common code for opening JSON object or JSON array: json_import_open: // limit nested levels: if (level >= JSON_MAXDEPTH) { @@ -204,16 +217,20 @@ // end of JSON object: case '}': // if end of JSON object is not expected here, then return an error: - if (mode != JSON_STATE_OBJECT_KEY && mode != JSON_STATE_OBJECT_SEPARATOR) - goto json_import_syntax_error; + if ( + mode != JSON_STATE_OBJECT_KEY && + mode != JSON_STATE_OBJECT_SEPARATOR + ) goto json_import_syntax_error; // jump to common code for end of JSON object and JSON array: goto json_import_close; // end of JSON array: case ']': // if end of JSON array is not expected here, then return an error: - if (mode != JSON_STATE_ARRAY_VALUE && mode != JSON_STATE_ARRAY_SEPARATOR) - goto json_import_syntax_error; - // pop nil key/magic: + if ( + mode != JSON_STATE_ARRAY_VALUE && + mode != JSON_STATE_ARRAY_SEPARATOR + ) goto json_import_syntax_error; + // pop nil key/magic (that was needed to keep stack balance): lua_pop(L, 1); // continue with common code for end of JSON object and JSON array: // common code for end of JSON object or JSON array: @@ -224,7 +241,8 @@ lua_pop(L, 1); // check if nested: if (--level) { - // if nested, then check if outer(!) structure is an array or object: + // if nested, + // check if outer(!) structure is an array or object: if (lua_isnil(L, -2)) { // select array value processing: mode = JSON_STATE_ARRAY_VALUE; @@ -245,15 +263,18 @@ goto json_import_syntax_error; // consume input character: pos++; - // set state of parser and continue with loop: + // expect object value to follow: mode = JSON_STATE_OBJECT_VALUE; + // continue with loop: goto json_import_loop; // value terminator (NOTE: trailing comma at end of value or key-value list is tolerated by this parser) case ',': - // change parser state accordingly: + // branch according to parser state: if (mode == JSON_STATE_OBJECT_SEPARATOR) { + // expect an object key to follow: mode = JSON_STATE_OBJECT_KEY; } else if (mode == JSON_STATE_ARRAY_SEPARATOR) { + // expect an array value to follow: mode = JSON_STATE_ARRAY_VALUE; } else { // if value terminator is not expected here, then return an error: @@ -265,15 +286,15 @@ goto json_import_loop; // string literal: case '"': + // consume quote character: + pos++; // prepare buffer to decode string (with maximum possible length) and set write position to zero: cbuf = luaL_buffinitsize(L, &luabuf, total-pos); writepos = 0; - // consume quote character: - pos++; - // read next character until encountering end quote: + // loop through the characters until encountering end quote: while ((c = str[pos++]) != '"') { if (c == 0) { - // handle unexpected end-of-string: + // handle unexpected end of JSON document: goto json_import_unexpected_eof; } else if (c < 32 || c == 127) { // do not allow ASCII control characters: @@ -296,25 +317,15 @@ cbuf[writepos++] = c; break; // unescaping of backspace: - case 'b': - cbuf[writepos++] = '\b'; - break; + case 'b': cbuf[writepos++] = '\b'; break; // unescaping of form-feed: - case 'f': - cbuf[writepos++] = '\f'; - break; + case 'f': cbuf[writepos++] = '\f'; break; // unescaping of new-line: - case 'n': - cbuf[writepos++] = '\n'; - break; + case 'n': cbuf[writepos++] = '\n'; break; // unescaping of carriage-return: - case 'r': - cbuf[writepos++] = '\r'; - break; + case 'r': cbuf[writepos++] = '\r'; break; // unescaping of tabulator: - case 't': - cbuf[writepos++] = '\t'; - break; + case 't': cbuf[writepos++] = '\t'; break; // unescaping of UTF-16 characters case 'u': lua_pushnil(L); @@ -338,12 +349,16 @@ } // process values whose type is is not deducible from a single character: if ((c >= '0' && c <= '9') || c == '-' || c == '+') { - // numbers: + // for numbers, + // use strtod() call to parse a (double precision) floating point number: char *endptr; double numval; numval = strtod(str+pos, &endptr); + // catch parsing errors: if (endptr == str+pos) goto json_import_syntax_error; + // consume characters that were parsed: pos += endptr - (str+pos); + // push parsed (double precision) floating point number on Lua stack: lua_pushnumber(L, numval); } else if (!strncmp(str+pos, "true", 4)) { // consume 4 input characters for "true": @@ -371,22 +386,25 @@ case JSON_STATE_OBJECT_KEY: // if an object key is not a string, then this is a syntax error: if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error; - // expect key terminator and continue with loop: + // expect key terminator to follow: mode = JSON_STATE_OBJECT_KEY_TERMINATOR; + // continue with loop: goto json_import_loop; // a key value pair has been read: case JSON_STATE_OBJECT_VALUE: // store key value pair in outer shadow table: lua_rawset(L, -3); - // expect value terminator (or end of object) and continue with loop: + // expect value terminator (or end of object) to follow: mode = JSON_STATE_OBJECT_SEPARATOR; + // continue with loop: goto json_import_loop; // an array value has been read: case JSON_STATE_ARRAY_VALUE: // store value in outer shadow table: lua_rawseti(L, -3, lua_rawlen(L, -3) + 1); - // expect value terminator (or end of object) and continue with loop: + // expect value terminator (or end of object) to follow: mode = JSON_STATE_ARRAY_SEPARATOR; + // continue with loop goto json_import_loop; // a single value has been read: case JSON_STATE_VALUE: @@ -394,52 +412,54 @@ mode = JSON_STATE_END; goto json_import_loop; } - // syntax error handling (only reachable by goto statement): + // syntax error handling (reachable by goto statement): json_import_syntax_error: lua_pushnil(L); lua_pushliteral(L, "Syntax error in JSON document"); return 2; } +// special Lua stack indicies for json_path function: #define json_path_shadowtbl_idx 1 #define json_path_nullmark_idx 2 + +// stack offset of arguments to json_path function: #define json_path_idxshift 2 -// gets a value or its type from a JSON document (first argument) -// optionally using a path (variable number of keys after first argument): +// gets a value or its type from a JSON document (passed as first argument) +// optionally using a path (passed as variable number of keys after first argument): static int json_path(lua_State *L, int type_mode) { - int stacktop; - int idx = 2 + json_path_idxshift; - // insert json_shadowtbl on stack at position 1: + int stacktop; // stack index of top of stack (after shifting) + int idx = 2 + json_path_idxshift; // stack index of current argument to process + // insert json_shadowtbl on stack at position 1 (shifting the arguments): json_regfetch(L, shadowtbl); lua_insert(L, 1); - // insert json_nullmark on stack at position 2: + // insert json_nullmark on stack at position 2 (shifting the arguments): json_pushlightref(L, nullmark); lua_insert(L, 2); - // store number of arguments: + // store stack index of top of stack: stacktop = lua_gettop(L); - // follow path, starting with first argument as "current value": + // use first argument as "current value" (stored on top of stack): lua_pushvalue(L, 1 + json_path_idxshift); - // process each "path key": + // process each "path key" (2nd argument and following arguments): while (idx <= stacktop) { - // if "current value" is nil, then the path cannot be walked and nil is returned: + // if "current value" (on top of stack) is nil, then the path cannot be walked and nil is returned: if (lua_isnil(L, -1)) return 1; // try to get shadow table of "current value": lua_pushvalue(L, -1); lua_rawget(L, json_path_shadowtbl_idx); if (lua_isnil(L, -1)) { // if no shadow table is found, - // drop nil from stack: - lua_pop(L, 1); if (lua_type(L, -1) == LUA_TTABLE) { - // if "current value" is a table, + // and if "current value" is a table, + // drop nil from stack: + lua_pop(L, 1); // get "next value" using the "path key": lua_pushvalue(L, idx++); lua_gettable(L, -2); } else { // if "current value" is not a table, - // then the path cannot be walked and nil is returned: - lua_pushnil(L); + // then the path cannot be walked and nil (already on top of stack) is returned: return 1; } } else { @@ -469,16 +489,19 @@ if (lua_getmetatable(L, -1)) { json_regfetch(L, objectmt); if (lua_rawequal(L, -2, -1)) { + // if value has metatable for JSON objects, // return string "object": lua_pushliteral(L, "object"); return 1; } json_regfetch(L, arraymt); if (lua_rawequal(L, -3, -1)) { - // return string "array": + // if value has metatable for JSON arrays, + // return string "object": lua_pushliteral(L, "array"); return 1; } + // remove 3 metatables (one of the value, two for comparison) from stack: lua_pop(L, 3); } // otherwise, get the Lua type: @@ -513,6 +536,7 @@ return 1; } +// special Lua stack indicies for json_setnull function: #define json_setnull_unknownmt_idx 3 #define json_setnull_objectmt_idx 4 #define json_setnull_arraymt_idx 5 @@ -562,6 +586,7 @@ return 1; } +// special Lua stack indicies for json_index function: #define json_index_nullmark_idx 3 #define json_index_shadowtbl_idx 4 @@ -590,6 +615,7 @@ return 1; } +// special Lua stack indicies for json_pairs_iterfunc function: #define json_pairs_iterfunc_nullmark_idx 3 #define json_pairs_iterfunc_shadowtbl_idx 4 @@ -616,6 +642,7 @@ return 3; } +// special Lua stack indicies for json_ipairs_iterfunc function: #define json_ipairs_iterfunc_nullmark_idx 3 #define json_ipairs_iterfunc_shadowtbl_idx 4