webmcp
changeset 146:7912a05ebc3f
Moved lua_pop(L, 1) call to conditional expression and added documentation in JSON library
author | jbe |
---|---|
date | Wed Jul 30 20:54:42 2014 +0200 (2014-07-30) |
parents | 0edc2f05c66a |
children | da5ab2c226dc |
files | libraries/json/json.c |
line diff
1.1 --- a/libraries/json/json.c Wed Jul 30 19:59:11 2014 +0200 1.2 +++ b/libraries/json/json.c Wed Jul 30 20:54:42 2014 +0200 1.3 @@ -15,10 +15,9 @@ 1.4 #define json_regfetch(L, x) (json_regfetchpointer(L, json_regpointer(x))) 1.5 #define json_regstore(L, x) (lua_pushlightuserdata(L, json_regpointer(x)), lua_pushvalue(L, -2), lua_rawset(L, LUA_REGISTRYINDEX)); 1.6 1.7 -// generate dummy memory addresses that represent Lua objects 1.8 -// directly via lightuserdata values (not using the Lua registry): 1.9 +// generate dummy memory addresses that represent non-modifiable lightuserdata (dummy) objects: 1.10 static struct { 1.11 - JSON_REGENT nullmark; // magic value to indicate JSON null value 1.12 + JSON_REGENT nullmark; // magic value to indicate JSON null value in shadow table 1.13 } json_reference; 1.14 1.15 1.16 @@ -132,7 +131,14 @@ 1.17 // main loop of parser: 1.18 json_import_loop: 1.19 // skip whitespace and store next character in variable 'c': 1.20 - while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++; 1.21 + while (c = str[pos], 1.22 + c == ' ' || 1.23 + c == '\f' || 1.24 + c == '\n' || 1.25 + c == '\r' || 1.26 + c == '\t' || 1.27 + c == '\v' 1.28 + ) pos++; 1.29 // switch statement to handle certain (single) characters: 1.30 switch (c) { 1.31 // handle end of JSON document: 1.32 @@ -148,8 +154,11 @@ 1.33 // new JSON object: 1.34 case '{': 1.35 // if a JSON object is not expected here, then return an error: 1.36 - if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE) 1.37 - goto json_import_syntax_error; 1.38 + if ( 1.39 + mode != JSON_STATE_VALUE && 1.40 + mode != JSON_STATE_OBJECT_VALUE && 1.41 + mode != JSON_STATE_ARRAY_VALUE 1.42 + ) goto json_import_syntax_error; 1.43 // create JSON object on stack: 1.44 lua_newtable(L); 1.45 // set metatable of JSON object: 1.46 @@ -157,18 +166,22 @@ 1.47 lua_setmetatable(L, -2); 1.48 // create internal shadow table on stack: 1.49 lua_newtable(L); 1.50 - // register internal shadow table (and cleanup stack afterwards): 1.51 + // register internal shadow table: 1.52 lua_pushvalue(L, -2); 1.53 lua_pushvalue(L, -2); 1.54 lua_rawset(L, json_import_shadowtbl_idx); 1.55 - // expect object key (or end of object) and continue with loop: 1.56 + // expect object key (or end of object) to follow: 1.57 mode = JSON_STATE_OBJECT_KEY; 1.58 + // jump to common code for opening JSON object and JSON array: 1.59 goto json_import_open; 1.60 // new JSON array: 1.61 case '[': 1.62 // if a JSON array is not expected here, then return an error: 1.63 - if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE) 1.64 - goto json_import_syntax_error; 1.65 + if ( 1.66 + mode != JSON_STATE_VALUE && 1.67 + mode != JSON_STATE_OBJECT_VALUE && 1.68 + mode != JSON_STATE_ARRAY_VALUE 1.69 + ) goto json_import_syntax_error; 1.70 // create JSON array on stack: 1.71 lua_newtable(L); 1.72 // set metatable of JSON array: 1.73 @@ -176,16 +189,16 @@ 1.74 lua_setmetatable(L, -2); 1.75 // create internal shadow table on stack: 1.76 lua_newtable(L); 1.77 - // register internal shadow table (and cleanup stack afterwards): 1.78 + // register internal shadow table: 1.79 lua_pushvalue(L, -2); 1.80 lua_pushvalue(L, -2); 1.81 lua_rawset(L, json_import_shadowtbl_idx); 1.82 // add nil as key (needed to keep stack balance) and as magic to detect arrays: 1.83 lua_pushnil(L); 1.84 - // expect array value (or end of array) and continue with loop: 1.85 + // expect array value (or end of array) to follow: 1.86 mode = JSON_STATE_ARRAY_VALUE; 1.87 // continue with common code for opening JSON object and JSON array: 1.88 - // commn code for opening JSON object or JSON array: 1.89 + // common code for opening JSON object or JSON array: 1.90 json_import_open: 1.91 // limit nested levels: 1.92 if (level >= JSON_MAXDEPTH) { 1.93 @@ -204,16 +217,20 @@ 1.94 // end of JSON object: 1.95 case '}': 1.96 // if end of JSON object is not expected here, then return an error: 1.97 - if (mode != JSON_STATE_OBJECT_KEY && mode != JSON_STATE_OBJECT_SEPARATOR) 1.98 - goto json_import_syntax_error; 1.99 + if ( 1.100 + mode != JSON_STATE_OBJECT_KEY && 1.101 + mode != JSON_STATE_OBJECT_SEPARATOR 1.102 + ) goto json_import_syntax_error; 1.103 // jump to common code for end of JSON object and JSON array: 1.104 goto json_import_close; 1.105 // end of JSON array: 1.106 case ']': 1.107 // if end of JSON array is not expected here, then return an error: 1.108 - if (mode != JSON_STATE_ARRAY_VALUE && mode != JSON_STATE_ARRAY_SEPARATOR) 1.109 - goto json_import_syntax_error; 1.110 - // pop nil key/magic: 1.111 + if ( 1.112 + mode != JSON_STATE_ARRAY_VALUE && 1.113 + mode != JSON_STATE_ARRAY_SEPARATOR 1.114 + ) goto json_import_syntax_error; 1.115 + // pop nil key/magic (that was needed to keep stack balance): 1.116 lua_pop(L, 1); 1.117 // continue with common code for end of JSON object and JSON array: 1.118 // common code for end of JSON object or JSON array: 1.119 @@ -224,7 +241,8 @@ 1.120 lua_pop(L, 1); 1.121 // check if nested: 1.122 if (--level) { 1.123 - // if nested, then check if outer(!) structure is an array or object: 1.124 + // if nested, 1.125 + // check if outer(!) structure is an array or object: 1.126 if (lua_isnil(L, -2)) { 1.127 // select array value processing: 1.128 mode = JSON_STATE_ARRAY_VALUE; 1.129 @@ -245,15 +263,18 @@ 1.130 goto json_import_syntax_error; 1.131 // consume input character: 1.132 pos++; 1.133 - // set state of parser and continue with loop: 1.134 + // expect object value to follow: 1.135 mode = JSON_STATE_OBJECT_VALUE; 1.136 + // continue with loop: 1.137 goto json_import_loop; 1.138 // value terminator (NOTE: trailing comma at end of value or key-value list is tolerated by this parser) 1.139 case ',': 1.140 - // change parser state accordingly: 1.141 + // branch according to parser state: 1.142 if (mode == JSON_STATE_OBJECT_SEPARATOR) { 1.143 + // expect an object key to follow: 1.144 mode = JSON_STATE_OBJECT_KEY; 1.145 } else if (mode == JSON_STATE_ARRAY_SEPARATOR) { 1.146 + // expect an array value to follow: 1.147 mode = JSON_STATE_ARRAY_VALUE; 1.148 } else { 1.149 // if value terminator is not expected here, then return an error: 1.150 @@ -265,15 +286,15 @@ 1.151 goto json_import_loop; 1.152 // string literal: 1.153 case '"': 1.154 + // consume quote character: 1.155 + pos++; 1.156 // prepare buffer to decode string (with maximum possible length) and set write position to zero: 1.157 cbuf = luaL_buffinitsize(L, &luabuf, total-pos); 1.158 writepos = 0; 1.159 - // consume quote character: 1.160 - pos++; 1.161 - // read next character until encountering end quote: 1.162 + // loop through the characters until encountering end quote: 1.163 while ((c = str[pos++]) != '"') { 1.164 if (c == 0) { 1.165 - // handle unexpected end-of-string: 1.166 + // handle unexpected end of JSON document: 1.167 goto json_import_unexpected_eof; 1.168 } else if (c < 32 || c == 127) { 1.169 // do not allow ASCII control characters: 1.170 @@ -296,25 +317,15 @@ 1.171 cbuf[writepos++] = c; 1.172 break; 1.173 // unescaping of backspace: 1.174 - case 'b': 1.175 - cbuf[writepos++] = '\b'; 1.176 - break; 1.177 + case 'b': cbuf[writepos++] = '\b'; break; 1.178 // unescaping of form-feed: 1.179 - case 'f': 1.180 - cbuf[writepos++] = '\f'; 1.181 - break; 1.182 + case 'f': cbuf[writepos++] = '\f'; break; 1.183 // unescaping of new-line: 1.184 - case 'n': 1.185 - cbuf[writepos++] = '\n'; 1.186 - break; 1.187 + case 'n': cbuf[writepos++] = '\n'; break; 1.188 // unescaping of carriage-return: 1.189 - case 'r': 1.190 - cbuf[writepos++] = '\r'; 1.191 - break; 1.192 + case 'r': cbuf[writepos++] = '\r'; break; 1.193 // unescaping of tabulator: 1.194 - case 't': 1.195 - cbuf[writepos++] = '\t'; 1.196 - break; 1.197 + case 't': cbuf[writepos++] = '\t'; break; 1.198 // unescaping of UTF-16 characters 1.199 case 'u': 1.200 lua_pushnil(L); 1.201 @@ -338,12 +349,16 @@ 1.202 } 1.203 // process values whose type is is not deducible from a single character: 1.204 if ((c >= '0' && c <= '9') || c == '-' || c == '+') { 1.205 - // numbers: 1.206 + // for numbers, 1.207 + // use strtod() call to parse a (double precision) floating point number: 1.208 char *endptr; 1.209 double numval; 1.210 numval = strtod(str+pos, &endptr); 1.211 + // catch parsing errors: 1.212 if (endptr == str+pos) goto json_import_syntax_error; 1.213 + // consume characters that were parsed: 1.214 pos += endptr - (str+pos); 1.215 + // push parsed (double precision) floating point number on Lua stack: 1.216 lua_pushnumber(L, numval); 1.217 } else if (!strncmp(str+pos, "true", 4)) { 1.218 // consume 4 input characters for "true": 1.219 @@ -371,22 +386,25 @@ 1.220 case JSON_STATE_OBJECT_KEY: 1.221 // if an object key is not a string, then this is a syntax error: 1.222 if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error; 1.223 - // expect key terminator and continue with loop: 1.224 + // expect key terminator to follow: 1.225 mode = JSON_STATE_OBJECT_KEY_TERMINATOR; 1.226 + // continue with loop: 1.227 goto json_import_loop; 1.228 // a key value pair has been read: 1.229 case JSON_STATE_OBJECT_VALUE: 1.230 // store key value pair in outer shadow table: 1.231 lua_rawset(L, -3); 1.232 - // expect value terminator (or end of object) and continue with loop: 1.233 + // expect value terminator (or end of object) to follow: 1.234 mode = JSON_STATE_OBJECT_SEPARATOR; 1.235 + // continue with loop: 1.236 goto json_import_loop; 1.237 // an array value has been read: 1.238 case JSON_STATE_ARRAY_VALUE: 1.239 // store value in outer shadow table: 1.240 lua_rawseti(L, -3, lua_rawlen(L, -3) + 1); 1.241 - // expect value terminator (or end of object) and continue with loop: 1.242 + // expect value terminator (or end of object) to follow: 1.243 mode = JSON_STATE_ARRAY_SEPARATOR; 1.244 + // continue with loop 1.245 goto json_import_loop; 1.246 // a single value has been read: 1.247 case JSON_STATE_VALUE: 1.248 @@ -394,52 +412,54 @@ 1.249 mode = JSON_STATE_END; 1.250 goto json_import_loop; 1.251 } 1.252 - // syntax error handling (only reachable by goto statement): 1.253 + // syntax error handling (reachable by goto statement): 1.254 json_import_syntax_error: 1.255 lua_pushnil(L); 1.256 lua_pushliteral(L, "Syntax error in JSON document"); 1.257 return 2; 1.258 } 1.259 1.260 +// special Lua stack indicies for json_path function: 1.261 #define json_path_shadowtbl_idx 1 1.262 #define json_path_nullmark_idx 2 1.263 + 1.264 +// stack offset of arguments to json_path function: 1.265 #define json_path_idxshift 2 1.266 1.267 -// gets a value or its type from a JSON document (first argument) 1.268 -// optionally using a path (variable number of keys after first argument): 1.269 +// gets a value or its type from a JSON document (passed as first argument) 1.270 +// optionally using a path (passed as variable number of keys after first argument): 1.271 static int json_path(lua_State *L, int type_mode) { 1.272 - int stacktop; 1.273 - int idx = 2 + json_path_idxshift; 1.274 - // insert json_shadowtbl on stack at position 1: 1.275 + int stacktop; // stack index of top of stack (after shifting) 1.276 + int idx = 2 + json_path_idxshift; // stack index of current argument to process 1.277 + // insert json_shadowtbl on stack at position 1 (shifting the arguments): 1.278 json_regfetch(L, shadowtbl); 1.279 lua_insert(L, 1); 1.280 - // insert json_nullmark on stack at position 2: 1.281 + // insert json_nullmark on stack at position 2 (shifting the arguments): 1.282 json_pushlightref(L, nullmark); 1.283 lua_insert(L, 2); 1.284 - // store number of arguments: 1.285 + // store stack index of top of stack: 1.286 stacktop = lua_gettop(L); 1.287 - // follow path, starting with first argument as "current value": 1.288 + // use first argument as "current value" (stored on top of stack): 1.289 lua_pushvalue(L, 1 + json_path_idxshift); 1.290 - // process each "path key": 1.291 + // process each "path key" (2nd argument and following arguments): 1.292 while (idx <= stacktop) { 1.293 - // if "current value" is nil, then the path cannot be walked and nil is returned: 1.294 + // if "current value" (on top of stack) is nil, then the path cannot be walked and nil is returned: 1.295 if (lua_isnil(L, -1)) return 1; 1.296 // try to get shadow table of "current value": 1.297 lua_pushvalue(L, -1); 1.298 lua_rawget(L, json_path_shadowtbl_idx); 1.299 if (lua_isnil(L, -1)) { 1.300 // if no shadow table is found, 1.301 - // drop nil from stack: 1.302 - lua_pop(L, 1); 1.303 if (lua_type(L, -1) == LUA_TTABLE) { 1.304 - // if "current value" is a table, 1.305 + // and if "current value" is a table, 1.306 + // drop nil from stack: 1.307 + lua_pop(L, 1); 1.308 // get "next value" using the "path key": 1.309 lua_pushvalue(L, idx++); 1.310 lua_gettable(L, -2); 1.311 } else { 1.312 // if "current value" is not a table, 1.313 - // then the path cannot be walked and nil is returned: 1.314 - lua_pushnil(L); 1.315 + // then the path cannot be walked and nil (already on top of stack) is returned: 1.316 return 1; 1.317 } 1.318 } else { 1.319 @@ -469,16 +489,19 @@ 1.320 if (lua_getmetatable(L, -1)) { 1.321 json_regfetch(L, objectmt); 1.322 if (lua_rawequal(L, -2, -1)) { 1.323 + // if value has metatable for JSON objects, 1.324 // return string "object": 1.325 lua_pushliteral(L, "object"); 1.326 return 1; 1.327 } 1.328 json_regfetch(L, arraymt); 1.329 if (lua_rawequal(L, -3, -1)) { 1.330 - // return string "array": 1.331 + // if value has metatable for JSON arrays, 1.332 + // return string "object": 1.333 lua_pushliteral(L, "array"); 1.334 return 1; 1.335 } 1.336 + // remove 3 metatables (one of the value, two for comparison) from stack: 1.337 lua_pop(L, 3); 1.338 } 1.339 // otherwise, get the Lua type: 1.340 @@ -513,6 +536,7 @@ 1.341 return 1; 1.342 } 1.343 1.344 +// special Lua stack indicies for json_setnull function: 1.345 #define json_setnull_unknownmt_idx 3 1.346 #define json_setnull_objectmt_idx 4 1.347 #define json_setnull_arraymt_idx 5 1.348 @@ -562,6 +586,7 @@ 1.349 return 1; 1.350 } 1.351 1.352 +// special Lua stack indicies for json_index function: 1.353 #define json_index_nullmark_idx 3 1.354 #define json_index_shadowtbl_idx 4 1.355 1.356 @@ -590,6 +615,7 @@ 1.357 return 1; 1.358 } 1.359 1.360 +// special Lua stack indicies for json_pairs_iterfunc function: 1.361 #define json_pairs_iterfunc_nullmark_idx 3 1.362 #define json_pairs_iterfunc_shadowtbl_idx 4 1.363 1.364 @@ -616,6 +642,7 @@ 1.365 return 3; 1.366 } 1.367 1.368 +// special Lua stack indicies for json_ipairs_iterfunc function: 1.369 #define json_ipairs_iterfunc_nullmark_idx 3 1.370 #define json_ipairs_iterfunc_shadowtbl_idx 4 1.371