webmcp

view libraries/json/json.c @ 125:e3e5bf890aad

Set json.metatable in JSON library for any JSON object/array
author jbe
date Sun Jul 27 01:51:45 2014 +0200 (2014-07-27)
parents ece4e6f683c9
children bccaa05aada7
line source
1 #include <lua.h>
2 #include <lauxlib.h>
3 #include <stdlib.h>
4 #include <string.h>
6 #define JSON_UPVAL_METATABLE lua_upvalueindex(1)
7 #define JSON_UPVAL_NULLS lua_upvalueindex(2)
8 #define JSON_UPVAL_ARYLEN lua_upvalueindex(3)
10 #define JSON_STATE_VALUE 0
11 #define JSON_STATE_OBJECT_KEY 1
12 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2
13 #define JSON_STATE_OBJECT_VALUE 3
14 #define JSON_STATE_OBJECT_SEPARATOR 4
15 #define JSON_STATE_ARRAY_VALUE 5
16 #define JSON_STATE_ARRAY_SEPARATOR 6
17 #define JSON_STATE_END 7
19 static int json_import(lua_State *L) {
20 const char *str;
21 size_t total;
22 size_t pos = 0;
23 size_t level = 0;
24 int mode = JSON_STATE_VALUE;
25 char c;
26 luaL_Buffer luabuf;
27 char *cbuf;
28 size_t writepos;
29 int aryidx;
30 lua_settop(L, 1);
31 str = lua_tostring(L, 1);
32 total = strlen(str);
33 json_import_loop:
34 while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++;
35 switch (c) {
36 case 0:
37 if (mode == JSON_STATE_END) return 1;
38 json_import_unexpected_eof:
39 lua_pushnil(L);
40 if (level == 0) lua_pushliteral(L, "Empty string");
41 else lua_pushliteral(L, "Unexpected end of JSON document");
42 return 2;
43 case '{':
44 if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE)
45 goto json_import_syntax_error;
46 pos++;
47 lua_newtable(L); // the actual JSON object
48 lua_pushvalue(L, JSON_UPVAL_METATABLE);
49 lua_setmetatable(L, -2);
50 lua_newtable(L); // stores set of NULL values
51 lua_pushvalue(L, -2);
52 lua_pushvalue(L, -2);
53 lua_rawset(L, JSON_UPVAL_NULLS);
54 mode = JSON_STATE_OBJECT_KEY;
55 level++;
56 goto json_import_loop;
57 case '[':
58 if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE)
59 goto json_import_syntax_error;
60 pos++;
61 lua_newtable(L); // the actual JSON array
62 lua_pushvalue(L, JSON_UPVAL_METATABLE);
63 lua_setmetatable(L, -2);
64 lua_newtable(L); // stores set of NULL values
65 lua_pushvalue(L, -2);
66 lua_pushvalue(L, -2);
67 lua_rawset(L, JSON_UPVAL_NULLS);
68 lua_pushinteger(L, 0); // length of array (since it may contain nil's)
69 mode = JSON_STATE_ARRAY_VALUE;
70 level++;
71 goto json_import_loop;
72 case '}':
73 if (mode != JSON_STATE_OBJECT_KEY && mode != JSON_STATE_OBJECT_SEPARATOR)
74 goto json_import_syntax_error;
75 goto json_import_close;
76 case ']':
77 if (mode != JSON_STATE_ARRAY_VALUE && mode != JSON_STATE_ARRAY_SEPARATOR)
78 goto json_import_syntax_error;
79 lua_pushvalue(L, -2); // use array table as key
80 lua_insert(L, -2); // use length of array as value
81 lua_rawset(L, JSON_UPVAL_ARYLEN); // store length in ephemeron table
82 // leaves array table on top of stack
83 json_import_close:
84 pos++;
85 lua_pop(L, 1); // pop table that stores set of NULL values
86 if (--level) {
87 if (lua_type(L, -2) == LUA_TNUMBER) {
88 mode = JSON_STATE_ARRAY_VALUE;
89 } else {
90 mode = JSON_STATE_OBJECT_VALUE;
91 }
92 goto json_import_process_value;
93 } else {
94 mode = JSON_STATE_END;
95 }
96 goto json_import_loop;
97 case ':':
98 if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR)
99 goto json_import_syntax_error;
100 pos++;
101 mode = JSON_STATE_OBJECT_VALUE;
102 goto json_import_loop;
103 case ',':
104 if (mode == JSON_STATE_OBJECT_SEPARATOR) {
105 mode = JSON_STATE_OBJECT_KEY;
106 } else if (mode == JSON_STATE_ARRAY_SEPARATOR) {
107 mode = JSON_STATE_ARRAY_VALUE;
108 } else {
109 goto json_import_syntax_error;
110 }
111 pos++;
112 goto json_import_loop;
113 case '"':
114 cbuf = luaL_buffinitsize(L, &luabuf, total-pos);
115 writepos = 0;
116 pos++;
117 while ((c = str[pos++]) != '"') {
118 if (c == 0) {
119 goto json_import_unexpected_eof;
120 } else if (c < 32 || c == 127) {
121 lua_pushnil(L);
122 lua_pushliteral(L, "Unexpected control character in JSON string");
123 return 2;
124 } else if (c == '\\') {
125 c = str[pos++];
126 switch (c) {
127 case 0:
128 goto json_import_unexpected_eof;
129 case '"':
130 case '/':
131 case '\\':
132 cbuf[writepos++] = c;
133 break;
134 case 'b':
135 cbuf[writepos++] = '\b';
136 break;
137 case 'f':
138 cbuf[writepos++] = '\f';
139 break;
140 case 'n':
141 cbuf[writepos++] = '\n';
142 break;
143 case 'r':
144 cbuf[writepos++] = '\r';
145 break;
146 case 't':
147 cbuf[writepos++] = '\t';
148 break;
149 case 'u':
150 lua_pushnil(L);
151 lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet"); // TODO
152 return 2;
153 default:
154 lua_pushnil(L);
155 lua_pushliteral(L, "Unexpected string escape sequence in JSON document");
156 return 2;
157 }
158 } else {
159 cbuf[writepos++] = c;
160 }
161 }
162 if (!c) goto json_import_unexpected_eof;
163 luaL_pushresultsize(&luabuf, writepos);
164 goto json_import_process_value;
165 }
166 if (c == '-' || (c >= '0' && c <= '9')) {
167 char *endptr;
168 double numval;
169 numval = strtod(str+pos, &endptr);
170 if (endptr == str+pos) goto json_import_syntax_error;
171 pos += endptr - (str+pos);
172 lua_pushnumber(L, numval);
173 } else if (!strncmp(str+pos, "true", 4)) {
174 lua_pushboolean(L, 1);
175 pos += 4;
176 } else if (!strncmp(str+pos, "false", 5)) {
177 lua_pushboolean(L, 0);
178 pos += 5;
179 } else if (!strncmp(str+pos, "null", 4)) {
180 lua_pushnil(L);
181 pos += 4;
182 } else {
183 goto json_import_syntax_error;
184 }
185 json_import_process_value:
186 switch (mode) {
187 case JSON_STATE_OBJECT_KEY:
188 if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error;
189 mode = JSON_STATE_OBJECT_KEY_TERMINATOR;
190 goto json_import_loop;
191 case JSON_STATE_OBJECT_VALUE:
192 if (lua_isnil(L, -1)) {
193 lua_pushvalue(L, -2);
194 lua_pushboolean(L, 1);
195 lua_rawset(L, -5);
196 }
197 lua_rawset(L, -4);
198 mode = JSON_STATE_OBJECT_SEPARATOR;
199 goto json_import_loop;
200 case JSON_STATE_ARRAY_VALUE:
201 aryidx = lua_tointeger(L, -2) + 1;
202 if (lua_isnil(L, -1)) {
203 lua_pushinteger(L, aryidx);
204 lua_pushboolean(L, 1);
205 lua_rawset(L, -5);
206 }
207 lua_rawseti(L, -4, aryidx);
208 lua_pop(L, 1);
209 lua_pushinteger(L, aryidx);
210 mode = JSON_STATE_ARRAY_SEPARATOR;
211 goto json_import_loop;
212 case JSON_STATE_VALUE:
213 mode = JSON_STATE_END;
214 goto json_import_loop;
215 }
216 json_import_syntax_error:
217 lua_pushnil(L);
218 lua_pushliteral(L, "Syntax error in JSON document");
219 return 2;
220 }
222 static int json_arylen(lua_State *L) {
223 lua_settop(L, 1);
224 lua_rawget(L, JSON_UPVAL_ARYLEN);
225 return 1;
226 }
228 static int json_isnull(lua_State *L) {
229 lua_pushvalue(L, 1);
230 lua_rawget(L, JSON_UPVAL_NULLS);
231 if (lua_isnil(L, -1)) goto json_isnull_false;
232 lua_pushvalue(L, 2);
233 lua_rawget(L, -2);
234 if (!lua_isnil(L, -1)) return 1;
235 json_isnull_false:
236 lua_pushboolean(L, 0);
237 return 1;
238 }
240 static const struct luaL_Reg json_module_functions[] = {
241 {"import", json_import},
242 {"arylen", json_arylen},
243 {"isnull", json_isnull},
244 {NULL, NULL}
245 };
247 int luaopen_json(lua_State *L) {
248 lua_newtable(L); // library table
249 lua_newtable(L); // metatable for JSON objects and JSON arrays
250 lua_pushvalue(L, -1);
251 lua_setfield(L, -3, "metatable");
252 lua_newtable(L); // ephemeron table to store a set of keys associated with JSON-null values
253 lua_newtable(L); // ephemeron table to store the length of arrays (that may contain nil's)
254 lua_newtable(L); // meta table for ephemeron table
255 lua_pushliteral(L, "__mode");
256 lua_pushliteral(L, "k");
257 lua_rawset(L, -3);
258 lua_pushvalue(L, -1);
259 lua_setmetatable(L, -3);
260 lua_setmetatable(L, -2);
261 luaL_setfuncs(L, json_module_functions, 3);
262 return 1;
263 }

Impressum / About Us