webmcp

view libraries/json/json.c @ 133:214e11b72907

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

Impressum / About Us