webmcp

annotate libraries/json/json.c @ 129:453d8f8fbace

Extended function json.type(...) to accept two arguments, to allow return of "null"
author jbe
date Sun Jul 27 15:03:21 2014 +0200 (2014-07-27)
parents c507f8d62931
children 209f8ce39c5a
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@127 6 #define JSON_UPVAL_TYPES lua_upvalueindex(1)
jbe@127 7 #define JSON_UPVAL_NULLS lua_upvalueindex(2)
jbe@127 8 #define JSON_UPVAL_METATABLE lua_upvalueindex(3)
jbe@123 9
jbe@124 10 #define JSON_STATE_VALUE 0
jbe@124 11 #define JSON_STATE_OBJECT_KEY 1
jbe@124 12 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2
jbe@124 13 #define JSON_STATE_OBJECT_VALUE 3
jbe@124 14 #define JSON_STATE_OBJECT_SEPARATOR 4
jbe@124 15 #define JSON_STATE_ARRAY_VALUE 5
jbe@124 16 #define JSON_STATE_ARRAY_SEPARATOR 6
jbe@124 17 #define JSON_STATE_END 7
jbe@121 18
jbe@121 19 static int json_import(lua_State *L) {
jbe@121 20 const char *str;
jbe@121 21 size_t total;
jbe@121 22 size_t pos = 0;
jbe@121 23 size_t level = 0;
jbe@124 24 int mode = JSON_STATE_VALUE;
jbe@121 25 char c;
jbe@121 26 luaL_Buffer luabuf;
jbe@121 27 char *cbuf;
jbe@121 28 size_t writepos;
jbe@121 29 int aryidx;
jbe@121 30 lua_settop(L, 1);
jbe@121 31 str = lua_tostring(L, 1);
jbe@121 32 total = strlen(str);
jbe@121 33 json_import_loop:
jbe@121 34 while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++;
jbe@121 35 switch (c) {
jbe@121 36 case 0:
jbe@124 37 if (mode == JSON_STATE_END) return 1;
jbe@121 38 json_import_unexpected_eof:
jbe@121 39 lua_pushnil(L);
jbe@121 40 if (level == 0) lua_pushliteral(L, "Empty string");
jbe@121 41 else lua_pushliteral(L, "Unexpected end of JSON document");
jbe@121 42 return 2;
jbe@121 43 case '{':
jbe@124 44 if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE)
jbe@121 45 goto json_import_syntax_error;
jbe@121 46 pos++;
jbe@125 47 lua_newtable(L); // the actual JSON object
jbe@125 48 lua_pushvalue(L, JSON_UPVAL_METATABLE);
jbe@125 49 lua_setmetatable(L, -2);
jbe@127 50 lua_pushvalue(L, -1);
jbe@127 51 lua_pushliteral(L, "object");
jbe@127 52 lua_rawset(L, JSON_UPVAL_TYPES);
jbe@123 53 lua_newtable(L); // stores set of NULL values
jbe@123 54 lua_pushvalue(L, -2);
jbe@123 55 lua_pushvalue(L, -2);
jbe@123 56 lua_rawset(L, JSON_UPVAL_NULLS);
jbe@124 57 mode = JSON_STATE_OBJECT_KEY;
jbe@121 58 level++;
jbe@121 59 goto json_import_loop;
jbe@121 60 case '[':
jbe@124 61 if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE)
jbe@121 62 goto json_import_syntax_error;
jbe@121 63 pos++;
jbe@125 64 lua_newtable(L); // the actual JSON array
jbe@125 65 lua_pushvalue(L, JSON_UPVAL_METATABLE);
jbe@125 66 lua_setmetatable(L, -2);
jbe@127 67 lua_pushvalue(L, -1);
jbe@127 68 lua_pushliteral(L, "array");
jbe@127 69 lua_rawset(L, JSON_UPVAL_TYPES);
jbe@123 70 lua_newtable(L); // stores set of NULL values
jbe@123 71 lua_pushvalue(L, -2);
jbe@123 72 lua_pushvalue(L, -2);
jbe@123 73 lua_rawset(L, JSON_UPVAL_NULLS);
jbe@121 74 lua_pushinteger(L, 0); // length of array (since it may contain nil's)
jbe@124 75 mode = JSON_STATE_ARRAY_VALUE;
jbe@121 76 level++;
jbe@121 77 goto json_import_loop;
jbe@121 78 case '}':
jbe@124 79 if (mode != JSON_STATE_OBJECT_KEY && mode != JSON_STATE_OBJECT_SEPARATOR)
jbe@121 80 goto json_import_syntax_error;
jbe@121 81 goto json_import_close;
jbe@121 82 case ']':
jbe@124 83 if (mode != JSON_STATE_ARRAY_VALUE && mode != JSON_STATE_ARRAY_SEPARATOR)
jbe@121 84 goto json_import_syntax_error;
jbe@126 85 lua_pop(L, 1); // pop length information
jbe@121 86 json_import_close:
jbe@121 87 pos++;
jbe@123 88 lua_pop(L, 1); // pop table that stores set of NULL values
jbe@121 89 if (--level) {
jbe@121 90 if (lua_type(L, -2) == LUA_TNUMBER) {
jbe@124 91 mode = JSON_STATE_ARRAY_VALUE;
jbe@121 92 } else {
jbe@124 93 mode = JSON_STATE_OBJECT_VALUE;
jbe@121 94 }
jbe@121 95 goto json_import_process_value;
jbe@121 96 } else {
jbe@124 97 mode = JSON_STATE_END;
jbe@121 98 }
jbe@121 99 goto json_import_loop;
jbe@121 100 case ':':
jbe@124 101 if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR)
jbe@121 102 goto json_import_syntax_error;
jbe@121 103 pos++;
jbe@124 104 mode = JSON_STATE_OBJECT_VALUE;
jbe@121 105 goto json_import_loop;
jbe@121 106 case ',':
jbe@124 107 if (mode == JSON_STATE_OBJECT_SEPARATOR) {
jbe@124 108 mode = JSON_STATE_OBJECT_KEY;
jbe@124 109 } else if (mode == JSON_STATE_ARRAY_SEPARATOR) {
jbe@124 110 mode = JSON_STATE_ARRAY_VALUE;
jbe@121 111 } else {
jbe@121 112 goto json_import_syntax_error;
jbe@121 113 }
jbe@121 114 pos++;
jbe@121 115 goto json_import_loop;
jbe@121 116 case '"':
jbe@121 117 cbuf = luaL_buffinitsize(L, &luabuf, total-pos);
jbe@121 118 writepos = 0;
jbe@121 119 pos++;
jbe@121 120 while ((c = str[pos++]) != '"') {
jbe@121 121 if (c == 0) {
jbe@121 122 goto json_import_unexpected_eof;
jbe@121 123 } else if (c < 32 || c == 127) {
jbe@121 124 lua_pushnil(L);
jbe@121 125 lua_pushliteral(L, "Unexpected control character in JSON string");
jbe@121 126 return 2;
jbe@121 127 } else if (c == '\\') {
jbe@121 128 c = str[pos++];
jbe@121 129 switch (c) {
jbe@121 130 case 0:
jbe@121 131 goto json_import_unexpected_eof;
jbe@121 132 case '"':
jbe@121 133 case '/':
jbe@121 134 case '\\':
jbe@121 135 cbuf[writepos++] = c;
jbe@121 136 break;
jbe@121 137 case 'b':
jbe@121 138 cbuf[writepos++] = '\b';
jbe@121 139 break;
jbe@121 140 case 'f':
jbe@121 141 cbuf[writepos++] = '\f';
jbe@121 142 break;
jbe@121 143 case 'n':
jbe@121 144 cbuf[writepos++] = '\n';
jbe@121 145 break;
jbe@121 146 case 'r':
jbe@121 147 cbuf[writepos++] = '\r';
jbe@121 148 break;
jbe@121 149 case 't':
jbe@121 150 cbuf[writepos++] = '\t';
jbe@121 151 break;
jbe@121 152 case 'u':
jbe@121 153 lua_pushnil(L);
jbe@121 154 lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet"); // TODO
jbe@121 155 return 2;
jbe@121 156 default:
jbe@121 157 lua_pushnil(L);
jbe@121 158 lua_pushliteral(L, "Unexpected string escape sequence in JSON document");
jbe@121 159 return 2;
jbe@121 160 }
jbe@121 161 } else {
jbe@121 162 cbuf[writepos++] = c;
jbe@121 163 }
jbe@121 164 }
jbe@121 165 if (!c) goto json_import_unexpected_eof;
jbe@121 166 luaL_pushresultsize(&luabuf, writepos);
jbe@121 167 goto json_import_process_value;
jbe@121 168 }
jbe@122 169 if (c == '-' || (c >= '0' && c <= '9')) {
jbe@122 170 char *endptr;
jbe@122 171 double numval;
jbe@122 172 numval = strtod(str+pos, &endptr);
jbe@122 173 if (endptr == str+pos) goto json_import_syntax_error;
jbe@122 174 pos += endptr - (str+pos);
jbe@122 175 lua_pushnumber(L, numval);
jbe@122 176 } else if (!strncmp(str+pos, "true", 4)) {
jbe@121 177 lua_pushboolean(L, 1);
jbe@121 178 pos += 4;
jbe@121 179 } else if (!strncmp(str+pos, "false", 5)) {
jbe@121 180 lua_pushboolean(L, 0);
jbe@121 181 pos += 5;
jbe@121 182 } else if (!strncmp(str+pos, "null", 4)) {
jbe@121 183 lua_pushnil(L);
jbe@121 184 pos += 4;
jbe@121 185 } else {
jbe@121 186 goto json_import_syntax_error;
jbe@121 187 }
jbe@121 188 json_import_process_value:
jbe@121 189 switch (mode) {
jbe@124 190 case JSON_STATE_OBJECT_KEY:
jbe@121 191 if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error;
jbe@124 192 mode = JSON_STATE_OBJECT_KEY_TERMINATOR;
jbe@121 193 goto json_import_loop;
jbe@124 194 case JSON_STATE_OBJECT_VALUE:
jbe@123 195 if (lua_isnil(L, -1)) {
jbe@123 196 lua_pushvalue(L, -2);
jbe@123 197 lua_pushboolean(L, 1);
jbe@123 198 lua_rawset(L, -5);
jbe@123 199 }
jbe@123 200 lua_rawset(L, -4);
jbe@124 201 mode = JSON_STATE_OBJECT_SEPARATOR;
jbe@121 202 goto json_import_loop;
jbe@124 203 case JSON_STATE_ARRAY_VALUE:
jbe@121 204 aryidx = lua_tointeger(L, -2) + 1;
jbe@123 205 if (lua_isnil(L, -1)) {
jbe@123 206 lua_pushinteger(L, aryidx);
jbe@123 207 lua_pushboolean(L, 1);
jbe@123 208 lua_rawset(L, -5);
jbe@123 209 }
jbe@123 210 lua_rawseti(L, -4, aryidx);
jbe@121 211 lua_pop(L, 1);
jbe@121 212 lua_pushinteger(L, aryidx);
jbe@124 213 mode = JSON_STATE_ARRAY_SEPARATOR;
jbe@121 214 goto json_import_loop;
jbe@124 215 case JSON_STATE_VALUE:
jbe@124 216 mode = JSON_STATE_END;
jbe@121 217 goto json_import_loop;
jbe@121 218 }
jbe@121 219 json_import_syntax_error:
jbe@121 220 lua_pushnil(L);
jbe@121 221 lua_pushliteral(L, "Syntax error in JSON document");
jbe@121 222 return 2;
jbe@121 223 }
jbe@121 224
jbe@121 225 static int json_arylen(lua_State *L) {
jbe@126 226 int lower, middle;
jbe@126 227 int upper = 1;
jbe@121 228 lua_settop(L, 1);
jbe@126 229 lua_pushvalue(L, 1);
jbe@126 230 lua_rawget(L, JSON_UPVAL_NULLS);
jbe@127 231 if (lua_isnil(L, 2)) goto json_arylen_default;
jbe@126 232 lua_pushnil(L);
jbe@127 233 if (!lua_next(L, 2)) goto json_arylen_default;
jbe@127 234 lua_settop(L, 2);
jbe@126 235 while (1) {
jbe@126 236 lua_rawgeti(L, 1, upper);
jbe@126 237 if (lua_isnil(L, -1)) {
jbe@126 238 lua_pop(L, 1);
jbe@126 239 lua_pushinteger(L, upper);
jbe@126 240 lua_rawget(L, 2);
jbe@126 241 }
jbe@126 242 if (lua_isnil(L, -1)) break;
jbe@126 243 lua_pop(L, 1);
jbe@126 244 upper *= 2;
jbe@126 245 // TODO: avoid integer overflow!
jbe@126 246 }
jbe@126 247 lua_pop(L, 1);
jbe@126 248 lower = upper / 2;
jbe@126 249 while (upper - lower > 1) {
jbe@126 250 middle = (lower + upper) / 2;
jbe@126 251 lua_rawgeti(L, 1, middle);
jbe@126 252 if (lua_isnil(L, -1)) {
jbe@126 253 lua_pop(L, 1);
jbe@126 254 lua_pushinteger(L, middle);
jbe@126 255 lua_rawget(L, 2);
jbe@126 256 }
jbe@126 257 if (lua_isnil(L, -1)) upper = middle;
jbe@126 258 else lower = middle;
jbe@126 259 lua_pop(L, 1);
jbe@126 260 }
jbe@126 261 lua_pushinteger(L, lower);
jbe@126 262 return 1;
jbe@126 263 json_arylen_default:
jbe@126 264 lua_pushinteger(L, lua_rawlen(L, 1));
jbe@123 265 return 1;
jbe@123 266 }
jbe@123 267
jbe@127 268 static int json_type(lua_State *L) {
jbe@129 269 if (lua_gettop(L) >= 2) {
jbe@129 270 lua_pushvalue(L, 1);
jbe@129 271 lua_rawget(L, JSON_UPVAL_NULLS);
jbe@129 272 lua_pushvalue(L, 2);
jbe@129 273 lua_rawget(L, -2);
jbe@129 274 if (lua_toboolean(L, -1)) {
jbe@129 275 lua_getmetatable(L, 1);
jbe@129 276 if (lua_rawequal(L, -1, JSON_UPVAL_METATABLE)) {
jbe@129 277 lua_pushliteral(L, "null");
jbe@129 278 return 1;
jbe@129 279 }
jbe@129 280 }
jbe@129 281 lua_settop(L, 2);
jbe@129 282 lua_rawget(L, 1);
jbe@129 283 } else {
jbe@129 284 lua_settop(L, 1);
jbe@129 285 }
jbe@129 286 lua_pushvalue(L, -1);
jbe@127 287 lua_rawget(L, JSON_UPVAL_TYPES);
jbe@129 288 if (lua_isnil(L, -1)) lua_pushstring(L, lua_typename(L, lua_type(L, -2)));
jbe@127 289 return 1;
jbe@127 290 }
jbe@127 291
jbe@123 292 static int json_isnull(lua_State *L) {
jbe@127 293 lua_settop(L, 2);
jbe@127 294 lua_getmetatable(L, 1);
jbe@127 295 if (!lua_rawequal(L, -1, JSON_UPVAL_METATABLE)) goto json_isnull_false;
jbe@123 296 lua_pushvalue(L, 1);
jbe@123 297 lua_rawget(L, JSON_UPVAL_NULLS);
jbe@123 298 if (lua_isnil(L, -1)) goto json_isnull_false;
jbe@123 299 lua_pushvalue(L, 2);
jbe@123 300 lua_rawget(L, -2);
jbe@123 301 if (!lua_isnil(L, -1)) return 1;
jbe@123 302 json_isnull_false:
jbe@123 303 lua_pushboolean(L, 0);
jbe@121 304 return 1;
jbe@121 305 }
jbe@121 306
jbe@121 307 static const struct luaL_Reg json_module_functions[] = {
jbe@121 308 {"import", json_import},
jbe@127 309 {"type", json_type},
jbe@123 310 {"isnull", json_isnull},
jbe@121 311 {NULL, NULL}
jbe@121 312 };
jbe@121 313
jbe@126 314 static const struct luaL_Reg json_metatable_functions[] = {
jbe@126 315 {"__len", json_arylen},
jbe@126 316 {NULL, NULL}
jbe@126 317 };
jbe@126 318
jbe@121 319 int luaopen_json(lua_State *L) {
jbe@126 320 lua_settop(L, 0);
jbe@126 321 lua_newtable(L); // 1: library table on stack position
jbe@127 322 lua_newtable(L); // 2: ephemeron table to store the type of the JSON object/array
jbe@127 323 lua_newtable(L); // 3: ephemeron table to store a set of keys associated with JSON-null values
jbe@127 324 lua_newtable(L); // 4: metatable for ephemeron tables
jbe@121 325 lua_pushliteral(L, "__mode");
jbe@121 326 lua_pushliteral(L, "k");
jbe@127 327 lua_rawset(L, 4);
jbe@127 328 lua_pushvalue(L, 4); // 5: cloned metatable reference
jbe@126 329 lua_setmetatable(L, 2);
jbe@127 330 lua_setmetatable(L, 3);
jbe@127 331 lua_newtable(L); // 4: metatable for JSON objects and JSON arrays
jbe@127 332 lua_pushvalue(L, 4);
jbe@126 333 lua_setfield(L, 1, "metatable");
jbe@126 334 lua_pushvalue(L, 2);
jbe@126 335 lua_pushvalue(L, 3);
jbe@127 336 lua_pushvalue(L, 4);
jbe@127 337 luaL_setfuncs(L, json_metatable_functions, 3);
jbe@127 338 luaL_setfuncs(L, json_module_functions, 3);
jbe@121 339 return 1;
jbe@121 340 }

Impressum / About Us