]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libjson/json.c
test: use libc.h constants for access() mode (thanks qrstuv)
[plan9front.git] / sys / src / libjson / json.c
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <json.h>
5
6 typedef struct Lex Lex;
7
8 enum {
9         TEOF,
10         TSTRING = Runemax+1,
11         TNUM,
12         TNULL,
13         TFALSE,
14         TTRUE,
15 };
16
17 struct Lex
18 {
19         char *s;
20         ulong slen;
21         int t;
22         double n;
23         char *buf;
24         Rune peeked;
25         jmp_buf jmp;
26         int canjmp;
27 };
28
29 static Rune
30 getch(Lex *l)
31 {
32         Rune r;
33
34         if(l->peeked){
35                 r = l->peeked;
36                 l->peeked = 0;
37                 return r;
38         }
39         if(l->s[0] == '\0')
40                 return 0;
41         l->s += chartorune(&r, l->s);
42         return r;
43 }
44
45 static Rune
46 peekch(Lex *l)
47 {
48         if(!l->peeked)
49                 l->peeked = getch(l);
50         return l->peeked;
51 }
52
53 static int
54 fixsurrogate(Rune *rp, Rune r2)
55 {
56         Rune r1;
57
58         r1 = *rp;
59         if(r1 >= 0xD800 && r1 <= 0xDBFF){
60                 if(r2 >= 0xDC00 && r2 <= 0xDFFF){
61                         *rp = 0x10000 + (((r1 - 0xD800)<<10) | (r2 - 0xDC00));
62                         return 0;
63                 }
64                 return 1;
65         } else
66         if(r1 >= 0xDC00 && r1 <= 0xDFFF){
67                 if(r2 >= 0xD800 && r2 <= 0xDBFF){
68                         *rp = 0x10000 + (((r2 - 0xD800)<<10) | (r1 - 0xDC00));
69                         return 0;
70                 }
71                 return 1;
72         }
73         return 0;
74 }
75
76 static int
77 lex(Lex *l)
78 {
79         Rune r, r2;
80         char *t;
81         int i;
82         char c;
83
84         for(;;){
85                 r = peekch(l);
86                 if(r != 0x20 && r != 0x09 && r != 0x0A && r != 0x0D)
87                         break;
88                 getch(l);
89         }
90         r = getch(l);
91         if(r == ']' && l->canjmp)
92                 longjmp(l->jmp, 1);
93         l->canjmp = 0;
94         if(r == 0 || r == '{' || r == '[' || r == ']' || r == '}' || r == ':' || r == ','){
95                 l->t = r;
96                 return 0;
97         }
98         if(r >= 0x80 || isalpha(r)){
99                 t = l->buf;
100                 for(;;){
101                         t += runetochar(t, &r);
102                         if(t >= l->buf + l->slen){
103                                 werrstr("json: literal too long");
104                                 return -1;
105                         }
106                         r = peekch(l);
107                         if(r < 0x80 && !isalpha(r))
108                                 break;
109                         getch(l);
110                 }
111                 *t = 0;
112                 if(strcmp(l->buf, "true") == 0)
113                         l->t = TTRUE;
114                 else if(strcmp(l->buf, "false") == 0)
115                         l->t = TFALSE;
116                 else if(strcmp(l->buf, "null") == 0)
117                         l->t = TNULL;
118                 else{
119                         werrstr("json: invalid literal");
120                         return -1;
121                 }
122                 return 0;
123         }
124         if(isdigit(r) || r == '-'){
125                 l->n = strtod(l->s-1, &l->s);
126                 l->t = TNUM;
127                 return 0;
128         }
129         if(r == '"'){
130                 r2 = 0;
131                 t = l->buf;
132                 for(;;){
133                         r = getch(l);
134                         if(r == '"')
135                                 break;
136                         if(r < ' '){
137                                 werrstr("json: invalid char in string %x", r);
138                                 return -1;
139                         }
140                         if(r == '\\'){
141                                 r = getch(l);
142                                 switch(r){
143                                 case 'n':
144                                         r = '\n';
145                                         break;
146                                 case 'r':
147                                         r = '\r';
148                                         break;
149                                 case 'u':
150                                         r = 0;
151                                         for(i = 0; i < 4; i++){
152                                                 if(!isxdigit(peekch(l)))
153                                                         break;
154
155                                                 c = getch(l);
156                                                 r *= 16;
157                                                 if(c >= '0' && c <= '9')
158                                                         r += c - '0';
159                                                 else if(c >= 'a' && c <= 'f')
160                                                         r += c - 'a' + 10;
161                                                 else if(c >= 'A' && c <= 'F')
162                                                         r += c - 'A' + 10;
163                                         }
164                                         if(fixsurrogate(&r, r2)){
165                                                 r2 = r;
166                                                 continue;
167                                         }
168                                         break;
169                                 case 't':
170                                         r = '\t';
171                                         break;
172                                 case 'f':
173                                         r = '\f';
174                                         break;
175                                 case 'b':
176                                         r = '\b';
177                                         break;
178                                 case '"': case '/': case '\\':
179                                         break;
180                                 default:
181                                         werrstr("json: invalid escape sequence \\%C", r);
182                                         return -1;
183                                 }
184                         }
185                         r2 = 0;
186                         t += runetochar(t, &r);
187                         if(t >= l->buf + l->slen){
188                                 werrstr("json: string too long");
189                                 return -1;
190                         }
191                 }
192                 *t = 0;
193                 l->t = TSTRING;
194                 return 0;
195         }
196         werrstr("json: invalid char %C", peekch(l));
197         return -1;
198 }
199
200 static JSON*
201 jsonobj(Lex *l)
202 {
203         JSON *j;
204         JSONEl *e;
205         JSONEl **ln;
206         int obj;
207
208         if((j = mallocz(sizeof(*j), 1)) == nil)
209                 return nil;
210
211         if(lex(l) < 0){
212 error:
213                 free(j);
214                 return nil;
215         }
216         switch(l->t){
217         case TEOF:
218                 werrstr("json: unexpected eof");
219                 goto error;
220         case TNULL:
221                 j->t = JSONNull;
222                 break;
223         case TTRUE:
224                 j->t = JSONBool;
225                 j->n = 1;
226                 break;
227         case TFALSE:
228                 j->t = JSONBool;
229                 j->n = 0;
230                 break;
231         case TSTRING:
232                 j->t = JSONString;
233                 if((j->s = strdup(l->buf)) == nil)
234                         goto error;
235                 break;
236         case TNUM:
237                 j->t = JSONNumber;
238                 j->n = l->n;
239                 break;
240         case '{':
241         case '[':
242                 obj = l->t == '{';
243                 ln = &j->first;
244                 e = nil;
245                 if(obj){
246                         j->t = JSONObject;
247                         if(lex(l) < 0)
248                                 goto abort;
249                         if(l->t == '}')
250                                 return j;
251                         goto firstobj;
252                 }else{
253                         j->t = JSONArray;
254                         l->canjmp = 1;
255                         if(setjmp(l->jmp) > 0){
256                                 free(e);
257                                 return j;
258                         }
259                 }
260                 for(;;){
261                         if(obj){
262                                 if(lex(l) < 0)
263                                         goto abort;
264                         firstobj:
265                                 if(l->t != TSTRING){
266                                         werrstr("json: syntax error, not string");
267                                         goto abort;
268                                 }
269                                 if((e = mallocz(sizeof(*e), 1)) == nil)
270                                         goto abort;
271                                 e->name = strdup(l->buf);
272                                 if(e->name == nil || lex(l) < 0){
273                                         free(e);
274                                         goto abort;
275                                 }
276                                 if(l->t != ':'){
277                                         werrstr("json: syntax error, not colon");
278                                         free(e);
279                                         goto abort;
280                                 }
281                         }else{
282                                 if((e = mallocz(sizeof(*e), 1)) == nil)
283                                         goto abort;
284                         }
285                         e->val = jsonobj(l);
286                         if(e->val == nil){
287                                 free(e);
288                                 goto abort;
289                         }
290                         *ln = e;
291                         ln = &e->next;
292                         if(lex(l) < 0)
293                                 goto abort;
294                         if(l->t == (obj ? '}' : ']'))
295                                 break;
296                         if(l->t != ','){
297                                 werrstr("json: syntax error, neither comma nor ending paren");
298                                 goto abort;
299                         }
300                 }
301                 break;
302         abort:
303                 jsonfree(j);
304                 return nil;
305         case ']': case '}': case ',': case ':':
306                 werrstr("json: unexpected %C", l->t);
307                 goto error;
308         default:
309                 werrstr("json: the front fell off");
310                 goto error;
311         }
312         return j;
313 }
314
315 JSON*
316 jsonparse(char *s)
317 {
318         JSON *j;
319         Lex l;
320
321         memset(&l, 0, sizeof(l));
322         l.s = s;
323         l.slen = strlen(s);
324         if((l.buf = mallocz(l.slen+1, 1)) == nil)
325                 return nil;
326
327         j = jsonobj(&l);
328         free(l.buf);
329         return j;
330 }
331
332 void
333 jsonfree(JSON *j)
334 {
335         JSONEl *e, *f;
336
337         switch(j->t){
338         case JSONString:
339                 if(j->s)
340                         free(j->s);
341                 break;
342         case JSONArray: case JSONObject:
343                 for(e = j->first; e != nil; e = f){
344                         if(e->name)
345                                 free(e->name);
346                         jsonfree(e->val);
347                         f = e->next;
348                         free(e);
349                 }
350         }
351         free(j);
352 }
353
354 JSON *
355 jsonbyname(JSON *j, char *n)
356 {
357         JSONEl *e;
358         
359         if(j->t != JSONObject){
360                 werrstr("not an object");
361                 return nil;
362         }
363         for(e = j->first; e != nil; e = e->next)
364                 if(strcmp(e->name, n) == 0)
365                         return e->val;
366         werrstr("key '%s' not found", n);
367         return nil;
368 }
369
370 char *
371 jsonstr(JSON *j)
372 {
373         if(j == nil)
374                 return nil;
375         if(j->t != JSONString){
376                 werrstr("not a string");
377                 return nil;
378         }
379         return j->s;
380 }