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