1 /* Copyright 2016, UCAR/Unidata.
2 See the LICENSE file for more information.
3 */
4
5 #ifndef NCJSON_INC
6 #define NCJSON_INC 1
7
8 #define NCJ_DICT 1
9 #define NCJ_LIST 2
10 #define NCJ_WORD 3
11 #define NCJ_NUMBER 4
12 #define NCJ_BOOLEAN 5
13
14 /* Don't bother with unions */
15 typedef struct NCjson {
16 long sort;
17 char* word; /* string or (!boolean && !number) */
18 long long num; /* number || boolean (0=>false; !0=>true)*/
19 NCjson* list;
20 } NCjson;
21
22 #define NCJ_LBRACKET '['
23 #define NCJ_RBRACKET ']'
24 #define NCJ_LBRACE '{'
25 #define NCJ_RBRACE '}'
26 #define NCJ_COLON ':'
27 #define NCJ_COMMA ','
28 #define NCJ_QUOTE '"'
29 #define NCJ_TRUE "true"
30 #define NCJ_FALSE "false"
31
32 #define NCJ_WORD "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-$"
33
34 /*//////////////////////////////////////////////////*/
35
36 typedef struct NCJparser {
37 char* text;
38 char* pos;
39 char* yytext;
40 int errno;
41 struct {
42 char* yytext;
43 int token;
44 } pushback;
45 } NCJparser;
46
47 static int
NCjsonparse(char * text,NCjson ** treep)48 NCjsonparse(char* text, NCjson** treep)
49 {
50 int status = NCJ_OK;
51 size_t len;
52 NCJparser parser = NULL;
53 NCjson* tree = NULL;
54 if(text == NULL) {status = NCJ_EINVAL; goto done;}
55 parser = calloc(1,sizeof(NCJparser));
56 if(parser == NULL) {status = NCJ_ENOMEM; goto done;}
57 len = strlen(text);
58 parser->text = (char*)malloc(len+1+1);
59 if(parser->text == NULL) {status = NCJ_ENOMEM; goto done;}
60 strcpy(parser->text,text);
61 parser->text[len] = '\0';
62 parser->text[len+1] = '\0';
63 tree = NCJparseR(parser);
64 done:
65 if(parser != NULL) {
66 nullfree(parser->text);
67 nullfree(parser->yytext);
68 free(parser);
69 }
70 if(status != NCJ_OK) {
71 if(tree != NULL) NCjsonfree(tree);
72 } else
73 if(treep) *treep = tree;
74 return status;
75 }
76
77 static int
NCJyytext(NCJparser * parser,char * start,ptrdiff_t pdlen)78 NCJyytext(NCJparser* parser, char* start, ptrdiff_t pdlen)
79 {
80 size_t len = (size_t)pdlen;
81 if(parser->yytext == NULL)
82 parser->yytext = (char*)malloc(len+1);
83 else
84 parser->yytext = (char*) realloc(parser->yytext,len+1);
85 if(parser->yytext == NULL) return NCJ_ENOMEM;
86 memcpy(parser->yytext,start,len);
87 parser->yytext[len] = NCJ_NUL;
88 return NCJ_OK;
89 }
90
91 static void
NCJpushback(NCJparser * parser,int token)92 NCJpushback(NCJparser* parser, int token)
93 {
94 parser->pushback.token = token;
95 parser->pushback.yytext = strdup(parser->yytext);
96 }
97
98 static int
NCJlex(NCJparser * parser)99 NCJlex(NCJparser* parser)
100 {
101 int c;
102 int token = NCJ_NUL;
103 char* start;
104 char* next;
105
106 if(parser->pushback.token != NCJ_NOTOKEN) {
107 token = parser->pushback.token;
108 NCJyytext(parser,parser->pushback.yytext,strlen(parser->pushback.yytext));
109 nullfree(parser->pushback.yytext);
110 parser->pushback.yytext = NULL;
111 parser->pushback.token = NCJ_NOTOKEN;
112 return token;
113 }
114
115 c = *parser->pos;
116 if(c == NCJ_NUL) {
117 token = NCJ_NUL;
118 } else if(strchr(NCJ_WORD, c) != NULL) {
119 size_t len;
120 start = parser->pos;
121 next = start + 1;
122 for(;;) {
123 c = *parser->pos++;
124 if(strchr(NCJ_WHITESPACE,c) != NULL || c == NCJ_NUL) break;
125 last++;
126 }
127 if(!NCJyytext(parser,start,(next - start))) goto done;
128 token = NCJ_WORD;
129 } else if(c == NCJ_QUOTE) {
130 parser->pos++;
131 start = parser->pos;
132 next = start+1;
133 for(;;) {
134 c = *parser->pos++;
135 if(c == NCJ_QUOTE || c == NCJ_NUL) break;
136 last++;
137 }
138 if(c == NCJ_NUL) {
139 parser->errno = NCJ_ESTRING;
140 token = NCJ_ERR;
141 goto done;
142 }
143 if(!NCJyytext(parser,start,(next - start))) goto done;
144 token = NCJ_STRING;
145 } else { /* single char token */
146 token = *parser->pos++;
147 }
148 done:
149 if(parser->errno) token = NCJ_ERR;
150 return token;
151 }
152
153 /* Simple recursive descent */
154
155 static int
NCJparseR(NCJparser * parser,NCjson ** listp)156 NCJparseR(NCJparser* parser, NCjson** listp)
157 {
158 int token = NCJ_ERR;
159 NCjson* list = NULL;
160 if((token = NCJlex(parser)) == NCJ_ERR) goto done;
161 switch (token) {
162 case NCJ_NUL;
163 break;
164 case NCJ_WORD:
165 NCJappend(NCJparseAtomic(parser,token),listp);
166 break;
167 case NCJ_LBRACE:
168 NCJappend(NCJparseMap(parser,locallist),listp);
169 break;
170 case NCJ_LBRACKET:
171 NCJappend(NCJparseArray(parser,NULL),)
172 case NCJ_STRING:
173 return NCJparseAtomic(parser,token);
174 default:
175 parser->errno = NCJ_EBADTOKEN;
176 }
177 return NULL;
178 }
179
180 static NCjson*
NCJparseAtomic(NCJparser * parser,int kind)181 NCJparseAtomic(NCJparser* parser, int kind)
182 {
183 /* assert (kind == NCJ_WORD || kind = NCJ_QUOTE) */
184 NCjson* node;
185 if((node = NCJmakenode(parser)) == NULL)
186 {parser->errno = NCJ_ENOMEM; goto done;}
187 if(kind == NCJ_STRING)
188 node->sort = NCJ_WORD;
189 node->word = strdup(parser->yytext);
190 } else {
191 /* Try to convert to number or boolean; last resort is word */
192 size_t count = (last - start) + 1;
193 int nread = 0;
194 int ncvt = sscan(parser->yytext,
195 "%L",&node->num,&nread);
196 if(ncvt == 1 && nread == count) {
197 node->sort = NCJ_NUMBER;
198 } else if(strcasecmp(parser->yytext,NCJ_TRUE)==0) {
199 node->sort = NCJ_BOOLEAN;
200 node->num = 1;
201 } else if(strcasecmp(parser->yytext,NCJ_FALSE)==0) {
202 node->sort = NCJ_BOOLEAN;
203 node->num = 0;
204 } else {
205 node->word = strdup(parser->yytext);
206 node->sort = NCJ_WORD;
207 }
208 }
209 done:
210 return node;
211 }
212
213 static NCjson*
214 NCJparseArray(NCJparser* parser)
215 {
216 NCjson* head = NULL;
217 NCjson* last = NULL;
218 int token = NCJ_ERR;
219 #if 0
220 if((node = NCJmakenode(parser)) == NULL) goto done;
221 #endif
222 loop:
223 for(;;) {
224 if((token = NCJlex(parser)) == NCJ_ERR) goto done;
225 switch (token) {
226 case NCJ_NUL;
227 break;
228 case NCJ_RBRACKET:
229 break loop;
230 default:
231 NCJpushback(parser,token);
232 NCjson* o = NCJparseR(parser);
233 tokens.nextToken();
234 if(tokens.ttype == NCJ_EOF) break;
235 else if(tokens.ttype == RBRACKET) tokens.pushBack();
236 else if(tokens.ttype != COMMA)
237 throw new IOException("Missing comma in list");
238 array.add(o);
239 }
240 }
241 return array;
242 }
243
244 static NCjson parseMap(StreamTokenizer tokens)
245 {
246 assert (tokens.ttype == LBRACE);
247 Map<char*, Object> map = new LinkedHashMap<>(); /* Keep insertion order */
248 loop:
249 for(; ; ) {
250 int token = tokens.nextToken();
251 switch (token) {
252 case NCJ_NCJ_EOL:
253 break; /* ignore */
254 case NCJ_NCJ_EOF:
255 throw new IOException("Unexpected eof");
256 case NCJ_RBRACE:
257 break loop;
258 default:
259 tokens.pushBack();
260 NCjson name = parseR(tokens);
261 if(tokens.ttype == NCJ_EOF) break;
262 if(name instanceof char*
263 || name instanceof Long
264 || name instanceof Boolean) {
265 /*ok*/
266 } else
267 throw new IOException("Unexpected map name type: " + name);
268 if(tokens.nextToken() != COLON)
269 throw new IOException("Expected ':'; found: " + tokens.ttype);
270 NCjson o = parseR(tokens);
271 tokens.nextToken();
272 if(tokens.ttype == NCJ_EOF) break;
273 else if(tokens.ttype == RBRACE) tokens.pushBack();
274 else if(tokens.ttype != COMMA)
275 throw new IOException("Missing comma in list");
276 map.put(name.tochar*(), o);
277 }
278 }
279 return map;
280 }
281 }
282
283 static char* tochar*(Object o) {return tochar*(o,"");}
284
285 static char* tochar*(Object o, char* demark)
286 {
287 char*Builder buf = new char*Builder();
288 tochar*R(o, buf, demark, 0);
289 return buf.tochar*();
290 }
291
292 static static void tochar*R(Object o, char*Builder buf, char* demark, int indent)
293 {
294 boolean first = true;
295 if(o instanceof List) {
296 List<Object> list = (List<Object>) o;
297 if(list.size()== 0) {
298 buf.append(LBRACKET);
299 buf.append(RBRACKET);
300 } else {
301 buf.append(LBRACKET);
302 buf.append('\n');
303 for(int i=0;i<list.size();i++) {
304 NCjson e = list.get(i);
305 buf.append(indent(indent));
306 tochar*R(e, buf, demark, indent + 2);
307 if(i < list.size()-1) buf.append(",");
308 buf.append("\n");
309 }
310 buf.append(indent(indent));
311 buf.append(RBRACKET);
312 }
313 } else if(o instanceof Map) {
314 Map<char*, Object> map = (Map<char*, Object>) o;
315 if(map.size() == 0) {
316 buf.append(LBRACE);
317 buf.append(RBRACE);
318 } else {
319 buf.append(LBRACE);
320 buf.append('\n');
321 int i = 0;
322 for(Map.Entry<char*, Object> e : map.entrySet()) {
323 buf.append(indent(indent + 2));
324 buf.append(QUOTE);
325 buf.append(e.getKey().replace("\"","\\\""));
326 buf.append(QUOTE);
327 buf.append(' ');
328 buf.append(COLON);
329 buf.append(' ');
330 tochar*R(e.getValue(), buf, demark, indent + 2);
331 if(i < map.size() - 1) buf.append(",");
332 buf.append("\n");
333 i++;
334 }
335 buf.append(indent(indent));
336 buf.append(RBRACE);
337 }
338 } else if((o instanceof Long) || (o instanceof Boolean)) {
339 buf.append(demark);
340 buf.append(o.tochar*());
341 buf.append(demark);
342 } else {
343 buf.append(QUOTE);
344 buf.append(o.tochar*().replace("\"","\\\""));
345 buf.append(QUOTE);
346 }
347 }
348
349 static char* blanks = " ";
350
351 static static char* indent(int n)
352 {
353 while(n > blanks.length()) {
354 blanks = blanks + blanks;
355 }
356 return blanks.substring(0, n);
357 }
358
359 }
360
361 static NCjson*
362 NCJmakenode(NCjsonparser* parser)
363 {
364 NCjson* node = NULL;
365 parser->errno = NCJ_OK;
366 node = (NCjson*)calloc(1,sizeof(NCjson));
367 if(node == null) parser->errno = NCJ_ENOMEM;
368 return node;
369 }
370
371
372 #endif /*NCJSON_INC*/
373