webmcp

view libraries/json/json.c @ 135:663722e35330

Added pairs metamethod for (sparse) JSON objects
author jbe
date Mon Jul 28 03:00:40 2014 +0200 (2014-07-28)
parents a1507b499fa5
children 3cf5fcf2bd5f
line source
1 #include <lua.h>
2 #include <lauxlib.h>
3 #include <stdlib.h>
4 #include <string.h>
6 #define JSON_UPVAL_NULLMARK lua_upvalueindex(1)
7 #define JSON_UPVAL_SHADOWTBL lua_upvalueindex(2)
8 #define JSON_UPVAL_TYPES lua_upvalueindex(3)
9 #define JSON_UPVAL_METATABLE lua_upvalueindex(4)
10 #define JSON_UPVAL_PAIRS_ITERFUNC lua_upvalueindex(5)
11 #define JSON_UPVAL_IPAIRS_ITERFUNC lua_upvalueindex(6)
13 #define JSON_STATE_VALUE 0
14 #define JSON_STATE_OBJECT_KEY 1
15 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2
16 #define JSON_STATE_OBJECT_VALUE 3
17 #define JSON_STATE_OBJECT_SEPARATOR 4
18 #define JSON_STATE_ARRAY_VALUE 5
19 #define JSON_STATE_ARRAY_SEPARATOR 6
20 #define JSON_STATE_END 7
22 static int json_object(lua_State *L) {
23 lua_settop(L, 1);
24 if (lua_isnil(L, 1)) {
25 lua_settop(L, 0);
26 lua_newtable(L);
27 }
28 lua_pushvalue(L, JSON_UPVAL_METATABLE);
29 lua_setmetatable(L, 1);
30 lua_pushvalue(L, 1);
31 lua_newtable(L); // internal shadow table
32 lua_rawset(L, JSON_UPVAL_SHADOWTBL);
33 lua_pushvalue(L, 1);
34 lua_pushliteral(L, "object");
35 lua_rawset(L, JSON_UPVAL_TYPES);
36 return 1;
37 }
39 static int json_array(lua_State *L) {
40 lua_settop(L, 1);
41 if (lua_isnil(L, 1)) {
42 lua_settop(L, 0);
43 lua_newtable(L);
44 }
45 lua_pushvalue(L, JSON_UPVAL_METATABLE);
46 lua_setmetatable(L, 1);
47 lua_pushvalue(L, 1);
48 lua_newtable(L); // internal shadow table
49 lua_rawset(L, JSON_UPVAL_SHADOWTBL);
50 lua_pushvalue(L, 1);
51 lua_pushliteral(L, "array");
52 lua_rawset(L, JSON_UPVAL_TYPES);
53 return 1;
54 }
56 static int json_import(lua_State *L) {
57 const char *str;
58 size_t total;
59 size_t pos = 0;
60 size_t level = 0;
61 int mode = JSON_STATE_VALUE;
62 char c;
63 luaL_Buffer luabuf;
64 char *cbuf;
65 size_t writepos;
66 lua_settop(L, 1);
67 str = lua_tostring(L, 1);
68 total = strlen(str);
69 json_import_loop:
70 while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++;
71 switch (c) {
72 case 0:
73 if (mode == JSON_STATE_END) return 1;
74 json_import_unexpected_eof:
75 lua_pushnil(L);
76 if (level == 0) lua_pushliteral(L, "Empty string");
77 else lua_pushliteral(L, "Unexpected end of JSON document");
78 return 2;
79 case '{':
80 if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE)
81 goto json_import_syntax_error;
82 pos++;
83 lua_newtable(L); // the external JSON object representation
84 lua_pushvalue(L, JSON_UPVAL_METATABLE);
85 lua_setmetatable(L, -2);
86 lua_pushvalue(L, -1);
87 lua_pushliteral(L, "object");
88 lua_rawset(L, JSON_UPVAL_TYPES);
89 lua_newtable(L); // the internal shadow table
90 lua_pushvalue(L, -2);
91 lua_pushvalue(L, -2);
92 lua_rawset(L, JSON_UPVAL_SHADOWTBL);
93 mode = JSON_STATE_OBJECT_KEY;
94 level++;
95 goto json_import_loop;
96 case '[':
97 if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE)
98 goto json_import_syntax_error;
99 pos++;
100 lua_newtable(L); // the external JSON array representation
101 lua_pushvalue(L, JSON_UPVAL_METATABLE);
102 lua_setmetatable(L, -2);
103 lua_pushvalue(L, -1);
104 lua_pushliteral(L, "array");
105 lua_rawset(L, JSON_UPVAL_TYPES);
106 lua_newtable(L); // the internal shadow table
107 lua_pushvalue(L, -2);
108 lua_pushvalue(L, -2);
109 lua_rawset(L, JSON_UPVAL_SHADOWTBL);
110 lua_pushinteger(L, 0); // magic integer to indicate an array
111 mode = JSON_STATE_ARRAY_VALUE;
112 level++;
113 goto json_import_loop;
114 case '}':
115 if (mode != JSON_STATE_OBJECT_KEY && mode != JSON_STATE_OBJECT_SEPARATOR)
116 goto json_import_syntax_error;
117 goto json_import_close;
118 case ']':
119 if (mode != JSON_STATE_ARRAY_VALUE && mode != JSON_STATE_ARRAY_SEPARATOR)
120 goto json_import_syntax_error;
121 lua_pop(L, 1); // pop magic integer
122 json_import_close:
123 pos++;
124 lua_pop(L, 1); // pop shadow table
125 if (--level) {
126 if (lua_type(L, -2) == LUA_TNUMBER) {
127 mode = JSON_STATE_ARRAY_VALUE;
128 } else {
129 mode = JSON_STATE_OBJECT_VALUE;
130 }
131 goto json_import_process_value;
132 } else {
133 mode = JSON_STATE_END;
134 }
135 goto json_import_loop;
136 case ':':
137 if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR)
138 goto json_import_syntax_error;
139 pos++;
140 mode = JSON_STATE_OBJECT_VALUE;
141 goto json_import_loop;
142 case ',':
143 if (mode == JSON_STATE_OBJECT_SEPARATOR) {
144 mode = JSON_STATE_OBJECT_KEY;
145 } else if (mode == JSON_STATE_ARRAY_SEPARATOR) {
146 mode = JSON_STATE_ARRAY_VALUE;
147 } else {
148 goto json_import_syntax_error;
149 }
150 pos++;
151 goto json_import_loop;
152 case '"':
153 cbuf = luaL_buffinitsize(L, &luabuf, total-pos);
154 writepos = 0;
155 pos++;
156 while ((c = str[pos++]) != '"') {
157 if (c == 0) {
158 goto json_import_unexpected_eof;
159 } else if (c < 32 || c == 127) {
160 lua_pushnil(L);
161 lua_pushliteral(L, "Unexpected control character in JSON string");
162 return 2;
163 } else if (c == '\\') {
164 c = str[pos++];
165 switch (c) {
166 case 0:
167 goto json_import_unexpected_eof;
168 case '"':
169 case '/':
170 case '\\':
171 cbuf[writepos++] = c;
172 break;
173 case 'b':
174 cbuf[writepos++] = '\b';
175 break;
176 case 'f':
177 cbuf[writepos++] = '\f';
178 break;
179 case 'n':
180 cbuf[writepos++] = '\n';
181 break;
182 case 'r':
183 cbuf[writepos++] = '\r';
184 break;
185 case 't':
186 cbuf[writepos++] = '\t';
187 break;
188 case 'u':
189 lua_pushnil(L);
190 lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet"); // TODO
191 return 2;
192 default:
193 lua_pushnil(L);
194 lua_pushliteral(L, "Unexpected string escape sequence in JSON document");
195 return 2;
196 }
197 } else {
198 cbuf[writepos++] = c;
199 }
200 }
201 if (!c) goto json_import_unexpected_eof;
202 luaL_pushresultsize(&luabuf, writepos);
203 goto json_import_process_value;
204 }
205 if (c == '-' || (c >= '0' && c <= '9')) {
206 char *endptr;
207 double numval;
208 numval = strtod(str+pos, &endptr);
209 if (endptr == str+pos) goto json_import_syntax_error;
210 pos += endptr - (str+pos);
211 lua_pushnumber(L, numval);
212 } else if (!strncmp(str+pos, "true", 4)) {
213 lua_pushboolean(L, 1);
214 pos += 4;
215 } else if (!strncmp(str+pos, "false", 5)) {
216 lua_pushboolean(L, 0);
217 pos += 5;
218 } else if (!strncmp(str+pos, "null", 4)) {
219 lua_pushvalue(L, JSON_UPVAL_NULLMARK);
220 pos += 4;
221 } else {
222 goto json_import_syntax_error;
223 }
224 json_import_process_value:
225 switch (mode) {
226 case JSON_STATE_OBJECT_KEY:
227 if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error;
228 mode = JSON_STATE_OBJECT_KEY_TERMINATOR;
229 goto json_import_loop;
230 case JSON_STATE_OBJECT_VALUE:
231 lua_rawset(L, -3);
232 mode = JSON_STATE_OBJECT_SEPARATOR;
233 goto json_import_loop;
234 case JSON_STATE_ARRAY_VALUE:
235 lua_rawseti(L, -3, lua_rawlen(L, -3) + 1);
236 mode = JSON_STATE_ARRAY_SEPARATOR;
237 goto json_import_loop;
238 case JSON_STATE_VALUE:
239 mode = JSON_STATE_END;
240 goto json_import_loop;
241 }
242 json_import_syntax_error:
243 lua_pushnil(L);
244 lua_pushliteral(L, "Syntax error in JSON document");
245 return 2;
246 }
248 #define JSON_PATH_GET 1
249 #define JSON_PATH_TYPE 2
250 #define JSON_PATH_ISNULL 3
252 static int json_path(lua_State *L, int mode) {
253 int argc;
254 int idx = 2;
255 argc = lua_gettop(L);
256 lua_pushvalue(L, 1);
257 while (idx <= argc) {
258 if (lua_isnil(L, -1)) {
259 if (mode == JSON_PATH_ISNULL) lua_pushboolean(L, 0);
260 return 1;
261 }
262 lua_pushvalue(L, -1);
263 lua_rawget(L, JSON_UPVAL_SHADOWTBL);
264 if (lua_isnil(L, -1)) {
265 lua_pop(L, 1);
266 if (lua_type(L, -1) == LUA_TTABLE) {
267 lua_pushvalue(L, idx++);
268 lua_gettable(L, -2);
269 } else {
270 lua_pushnil(L);
271 }
272 } else {
273 lua_replace(L, -2);
274 lua_pushvalue(L, idx++);
275 lua_rawget(L, -2);
276 }
277 lua_replace(L, -2);
278 }
279 switch (mode) {
280 case JSON_PATH_GET:
281 if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) lua_pushnil(L);
282 return 1;
283 case JSON_PATH_TYPE:
284 if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) {
285 lua_pushliteral(L, "null");
286 return 1;
287 }
288 lua_pushvalue(L, -1);
289 lua_rawget(L, JSON_UPVAL_TYPES);
290 if (lua_isnil(L, -1)) lua_pushstring(L, lua_typename(L, lua_type(L, -2)));
291 return 1;
292 case JSON_PATH_ISNULL:
293 lua_pushboolean(L, lua_rawequal(L, -1, JSON_UPVAL_NULLMARK));
294 return 1;
295 }
296 return 0;
297 }
299 static int json_get(lua_State *L) {
300 return json_path(L, JSON_PATH_GET);
301 }
303 static int json_type(lua_State *L) {
304 return json_path(L, JSON_PATH_TYPE);
305 }
307 static int json_isnull(lua_State *L) {
308 return json_path(L, JSON_PATH_ISNULL);
309 }
311 static int json_setnull(lua_State *L) {
312 lua_settop(L, 2);
313 lua_pushvalue(L, JSON_UPVAL_METATABLE);
314 lua_setmetatable(L, 1);
315 lua_pushvalue(L, 1);
316 lua_rawget(L, JSON_UPVAL_SHADOWTBL);
317 if (lua_isnil(L, -1)) {
318 lua_newtable(L);
319 lua_pushvalue(L, 1);
320 lua_pushvalue(L, -2);
321 lua_rawset(L, JSON_UPVAL_SHADOWTBL);
322 }
323 lua_pushvalue(L, 2);
324 lua_pushvalue(L, JSON_UPVAL_NULLMARK);
325 lua_rawset(L, -3);
326 return 0;
327 }
329 static int json_len(lua_State *L) {
330 lua_settop(L, 1);
331 lua_pushvalue(L, 1);
332 lua_rawget(L, JSON_UPVAL_SHADOWTBL);
333 if (lua_isnil(L, -1)) lua_pop(L, 1);
334 lua_pushinteger(L, lua_rawlen(L, -1));
335 return 1;
336 }
338 static int json_index(lua_State *L) {
339 lua_settop(L, 2);
340 lua_pushvalue(L, 1);
341 lua_rawget(L, JSON_UPVAL_SHADOWTBL);
342 if (lua_isnil(L, -1)) return 1;
343 lua_pushvalue(L, 2);
344 lua_rawget(L, -2);
345 if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) lua_pushnil(L);
346 return 1;
347 }
349 static int json_newindex(lua_State *L) {
350 lua_settop(L, 3);
351 lua_pushvalue(L, 1);
352 lua_rawget(L, JSON_UPVAL_SHADOWTBL);
353 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
354 lua_replace(L, 1);
355 lua_rawset(L, 1);
356 return 1;
357 }
359 static int json_pairs_iterfunc(lua_State *L) {
360 lua_settop(L, 2);
361 lua_pushvalue(L, 1);
362 lua_rawget(L, JSON_UPVAL_SHADOWTBL);
363 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
364 lua_pushvalue(L, 2);
365 if (!lua_next(L, -2)) return 0;
366 if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) {
367 lua_pop(L, 1);
368 lua_pushnil(L);
369 }
370 return 2;
371 }
373 static int json_pairs(lua_State *L) {
374 lua_pushvalue(L, JSON_UPVAL_PAIRS_ITERFUNC);
375 lua_pushvalue(L, 1);
376 lua_pushnil(L);
377 return 3;
378 }
380 static int json_ipairs_iterfunc(lua_State *L) {
381 int idx;
382 lua_settop(L, 2);
383 idx = lua_tointeger(L, 2) + 1;
384 lua_pushvalue(L, 1);
385 lua_rawget(L, JSON_UPVAL_SHADOWTBL);
386 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
387 lua_rawgeti(L, -1, idx);
388 if (lua_isnil(L, -1)) return 0;
389 lua_pushinteger(L, idx);
390 if (lua_rawequal(L, -2, JSON_UPVAL_NULLMARK)) lua_pushnil(L);
391 else lua_pushvalue(L, -2);
392 return 2;
393 }
395 static int json_ipairs(lua_State *L) {
396 lua_pushvalue(L, JSON_UPVAL_IPAIRS_ITERFUNC);
397 lua_pushvalue(L, 1);
398 lua_pushinteger(L, 0);
399 return 3;
400 }
402 static const struct luaL_Reg json_module_functions[] = {
403 {"object", json_object},
404 {"array", json_array},
405 {"import", json_import},
406 {"get", json_get},
407 {"type", json_type},
408 {"isnull", json_isnull},
409 {"setnull", json_setnull},
410 {NULL, NULL}
411 };
413 static const struct luaL_Reg json_metatable_functions[] = {
414 {"__len", json_len},
415 {"__index", json_index},
416 {"__newindex", json_newindex},
417 {"__pairs", json_pairs},
418 {"__ipairs", json_ipairs},
419 {NULL, NULL}
420 };
422 int luaopen_json(lua_State *L) {
423 lua_settop(L, 0);
424 lua_newtable(L); // 1: library table on stack position
425 lua_newtable(L); // 2: table used as JSON NULL value in internal shadow tables
426 lua_newtable(L); // 3: ephemeron table to store shadow tables for each JSON object/array to allow NULL values returned as nil
427 lua_newtable(L); // 4: ephemeron table to store the type of the JSON object/array
428 lua_newtable(L); // 5: metatable for ephemeron tables
429 lua_pushliteral(L, "__mode");
430 lua_pushliteral(L, "k");
431 lua_rawset(L, 5);
432 lua_pushvalue(L, 5); // 6: cloned metatable reference
433 lua_setmetatable(L, 3);
434 lua_setmetatable(L, 4);
435 lua_newtable(L); // 5: metatable for JSON objects and JSON arrays
436 lua_pushvalue(L, 2);
437 lua_pushvalue(L, 3);
438 lua_pushvalue(L, 4);
439 lua_pushvalue(L, 5);
440 lua_pushcclosure(L, json_pairs_iterfunc, 4); // 6: iteration function for pairs
441 lua_pushvalue(L, 2);
442 lua_pushvalue(L, 3);
443 lua_pushvalue(L, 4);
444 lua_pushvalue(L, 5);
445 lua_pushcclosure(L, json_ipairs_iterfunc, 4); // 7: iteration function for ipairs
446 lua_pushvalue(L, 5);
447 lua_pushvalue(L, 2);
448 lua_pushvalue(L, 3);
449 lua_pushvalue(L, 4);
450 lua_pushvalue(L, 5);
451 lua_pushvalue(L, 6);
452 lua_pushvalue(L, 7);
453 luaL_setfuncs(L, json_metatable_functions, 6);
454 lua_setfield(L, 1, "metatable");
455 luaL_setfuncs(L, json_module_functions, 6);
456 return 1;
457 }

Impressum / About Us