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