webmcp

view libraries/json/json.c @ 134:a1507b499fa5

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

Impressum / About Us