webmcp

view libraries/json/json.c @ 121:9ad1165cf3a1

Started work on a JSON library
author jbe
date Fri Jul 25 22:25:53 2014 +0200 (2014-07-25)
parents
children ff39d4a310b9
line source
1 #include <lua.h>
2 #include <lauxlib.h>
3 #include <string.h>
5 #define JSON_VALUE 0
6 #define JSON_OBJECT_KEY 1
7 #define JSON_OBJECT_KEY_TERMINATOR 2
8 #define JSON_OBJECT_VALUE 3
9 #define JSON_OBJECT_SEPARATOR 4
10 #define JSON_ARRAY_VALUE 5
11 #define JSON_ARRAY_SEPARATOR 6
12 #define JSON_END 7
14 static int json_import(lua_State *L) {
15 const char *str;
16 size_t total;
17 size_t pos = 0;
18 size_t level = 0;
19 int mode = JSON_VALUE;
20 char c;
21 luaL_Buffer luabuf;
22 char *cbuf;
23 size_t writepos;
24 int aryidx;
25 lua_settop(L, 1);
26 str = lua_tostring(L, 1);
27 total = strlen(str);
28 json_import_loop:
29 while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++;
30 switch (c) {
31 case 0:
32 if (mode == JSON_END) return 1;
33 json_import_unexpected_eof:
34 lua_pushnil(L);
35 if (level == 0) lua_pushliteral(L, "Empty string");
36 else lua_pushliteral(L, "Unexpected end of JSON document");
37 return 2;
38 case '{':
39 if (mode != JSON_VALUE && mode != JSON_OBJECT_VALUE && mode != JSON_ARRAY_VALUE)
40 goto json_import_syntax_error;
41 pos++;
42 lua_newtable(L);
43 mode = JSON_OBJECT_KEY;
44 level++;
45 goto json_import_loop;
46 case '[':
47 if (mode != JSON_VALUE && mode != JSON_OBJECT_VALUE && mode != JSON_ARRAY_VALUE)
48 goto json_import_syntax_error;
49 pos++;
50 lua_newtable(L);
51 lua_pushinteger(L, 0); // length of array (since it may contain nil's)
52 mode = JSON_ARRAY_VALUE;
53 level++;
54 goto json_import_loop;
55 case '}':
56 if (mode != JSON_OBJECT_KEY && mode != JSON_OBJECT_SEPARATOR)
57 goto json_import_syntax_error;
58 goto json_import_close;
59 case ']':
60 if (mode != JSON_ARRAY_VALUE && mode != JSON_ARRAY_SEPARATOR)
61 goto json_import_syntax_error;
62 lua_pushvalue(L, -2); // use array table as key
63 lua_insert(L, -2); // use length of array as value
64 lua_rawset(L, lua_upvalueindex(1)); // store length in ephemeron table
65 // leaves array table on top of stack
66 json_import_close:
67 pos++;
68 if (--level) {
69 if (lua_type(L, -2) == LUA_TNUMBER) {
70 mode = JSON_ARRAY_VALUE;
71 } else {
72 mode = JSON_OBJECT_VALUE;
73 }
74 goto json_import_process_value;
75 } else {
76 mode = JSON_END;
77 }
78 goto json_import_loop;
79 case ':':
80 if (mode != JSON_OBJECT_KEY_TERMINATOR)
81 goto json_import_syntax_error;
82 pos++;
83 mode = JSON_OBJECT_VALUE;
84 goto json_import_loop;
85 case ',':
86 if (mode == JSON_OBJECT_SEPARATOR) {
87 mode = JSON_OBJECT_KEY;
88 } else if (mode == JSON_ARRAY_SEPARATOR) {
89 mode = JSON_ARRAY_VALUE;
90 } else {
91 goto json_import_syntax_error;
92 }
93 pos++;
94 goto json_import_loop;
95 case '"':
96 cbuf = luaL_buffinitsize(L, &luabuf, total-pos);
97 writepos = 0;
98 pos++;
99 while ((c = str[pos++]) != '"') {
100 if (c == 0) {
101 goto json_import_unexpected_eof;
102 } else if (c < 32 || c == 127) {
103 lua_pushnil(L);
104 lua_pushliteral(L, "Unexpected control character in JSON string");
105 return 2;
106 } else if (c == '\\') {
107 c = str[pos++];
108 switch (c) {
109 case 0:
110 goto json_import_unexpected_eof;
111 case '"':
112 case '/':
113 case '\\':
114 cbuf[writepos++] = c;
115 break;
116 case 'b':
117 cbuf[writepos++] = '\b';
118 break;
119 case 'f':
120 cbuf[writepos++] = '\f';
121 break;
122 case 'n':
123 cbuf[writepos++] = '\n';
124 break;
125 case 'r':
126 cbuf[writepos++] = '\r';
127 break;
128 case 't':
129 cbuf[writepos++] = '\t';
130 break;
131 case 'u':
132 lua_pushnil(L);
133 lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet"); // TODO
134 return 2;
135 default:
136 lua_pushnil(L);
137 lua_pushliteral(L, "Unexpected string escape sequence in JSON document");
138 return 2;
139 }
140 } else {
141 cbuf[writepos++] = c;
142 }
143 }
144 if (!c) goto json_import_unexpected_eof;
145 luaL_pushresultsize(&luabuf, writepos);
146 goto json_import_process_value;
147 }
148 if (!strncmp(str+pos, "true", 4)) {
149 lua_pushboolean(L, 1);
150 pos += 4;
151 } else if (!strncmp(str+pos, "false", 5)) {
152 lua_pushboolean(L, 0);
153 pos += 5;
154 } else if (!strncmp(str+pos, "null", 4)) {
155 lua_pushnil(L);
156 pos += 4;
157 } else {
158 goto json_import_syntax_error;
159 }
160 json_import_process_value:
161 switch (mode) {
162 case JSON_OBJECT_KEY:
163 if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error;
164 mode = JSON_OBJECT_KEY_TERMINATOR;
165 goto json_import_loop;
166 case JSON_OBJECT_VALUE:
167 lua_rawset(L, -3);
168 mode = JSON_OBJECT_SEPARATOR;
169 goto json_import_loop;
170 case JSON_ARRAY_VALUE:
171 aryidx = lua_tointeger(L, -2) + 1;
172 lua_rawseti(L, -3, aryidx);
173 lua_pop(L, 1);
174 lua_pushinteger(L, aryidx);
175 mode = JSON_ARRAY_SEPARATOR;
176 goto json_import_loop;
177 case JSON_VALUE:
178 mode = JSON_END;
179 goto json_import_loop;
180 }
181 json_import_syntax_error:
182 lua_pushnil(L);
183 lua_pushliteral(L, "Syntax error in JSON document");
184 return 2;
185 }
187 static int json_arylen(lua_State *L) {
188 lua_settop(L, 1);
189 lua_rawget(L, lua_upvalueindex(1));
190 return 1;
191 }
193 static const struct luaL_Reg json_module_functions[] = {
194 {"import", json_import},
195 {"arylen", json_arylen},
196 {NULL, NULL}
197 };
199 int luaopen_json(lua_State *L) {
200 lua_newtable(L); // library table
201 lua_newtable(L); // ephemeron table to store the length of arrays (that may contain nil's)
202 lua_newtable(L); // meta table for ephemeron table
203 lua_pushliteral(L, "__mode");
204 lua_pushliteral(L, "k");
205 lua_rawset(L, -3);
206 lua_setmetatable(L, -2);
207 luaL_setfuncs(L, json_module_functions, 1);
208 return 1;
209 }

Impressum / About Us