2 Copyright (C) 2021 David Knapp (Cloudwalk)
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #include "darkplaces.h"
24 typedef enum qjson_type_e
26 JSON_TYPE_UNDEFINED = 0,
30 JSON_TYPE_USTRING = 4,
36 typedef struct qjson_token_s
40 struct qjson_token_s *parent;
42 llist_t list; // Array elements or key-value pairs
43 llist_t clist; // Head of list for children key-value pairs
55 static inline qjson_token_t *Json_Parse_Object(struct qparser_state_s *state, qjson_token_t *, qjson_token_t *);
56 static inline qjson_token_t *Json_Parse_Array(struct qparser_state_s *state, qjson_token_t *, qjson_token_t *);
57 static inline qjson_token_t *Json_Parse_Terminator(struct qparser_state_s *state, qjson_token_t *, qjson_token_t *);
59 // Checks for C/C++-style comments and ignores them. This is not standard json.
60 static qbool Json_Parse_Comment_SingleLine(struct qparser_state_s *state)
62 if(*state->pos == '/')
64 // FIXME: Let the parser interface increment this?
65 if(*state->pos++ == '/')
68 Parse_Error(state, PARSE_ERR_INVAL, "// or /*");
73 static qbool Json_Parse_CheckComment_Multiline_Start(struct qparser_state_s *state)
75 if(*state->pos == '/')
77 // FIXME: Let the parser interface increment this?
78 if(*state->pos++ == '*')
81 Parse_Error(state, PARSE_ERR_INVAL, "// or /*");
86 static qbool Json_Parse_CheckComment_Multiline_End(struct qparser_state_s *state)
88 if(*state->pos == '*')
90 // FIXME: Let the parser interface increment this?
91 if(*state->pos++ == '/')
97 static inline qjson_token_t *Json_Parse_NewToken(qparser_state_t *json, qjson_token_t *parent)
100 token = (qjson_token_t *)Z_Malloc(sizeof(qjson_token_t));
102 List_Add_Tail(&token->list, &parent->clist);
103 token->parent = parent;
107 static inline char Json_Parse_String_Escape(qparser_state_t *json, char escape)
114 // These can be returned literally
127 Parse_Error(json, PARSE_ERR_INVAL, "a valid escape sequence");
132 static inline qjson_token_t *Json_Parse_String(struct qparser_state_s *json, qjson_token_t *parent, qjson_token_t *token)
135 const char *start, *end;
143 while(*json->pos != '"')
145 if(*json->pos == '\\')
148 if(json->pos[1] == 'u')
150 Parse_Error(json, PARSE_ERR_INVAL, "Json Unicode escapes (\\u) are not supported");
162 token->string = (char *)Z_Malloc(((end - start) - subtract));
164 // Actually copy stuff over. 'i' should never exceed end - start.
165 for(i = 0; start != end; i++, start++)
170 token->string[i] = Json_Parse_String_Escape(json, *start);
173 token->string[i] = *start;
177 token->type = JSON_TYPE_STRING;
179 return Json_Parse_Terminator(json, parent, NULL);
182 // Handles numbers. Json numbers can be either an integer or a double.
183 static inline qjson_token_t *Json_Parse_Number(struct qparser_state_s *json, qjson_token_t *parent, qjson_token_t *token)
185 const char *lookahead = json->pos;
187 // First, figure out where the cursor should end up after atof.
188 // We don't care if the number is valid right now. atof takes care of that.
189 while(isdigit(*lookahead) || *lookahead == '-' || *lookahead == '+' || *lookahead == 'E' || *lookahead == 'e' || *lookahead == '.')
192 token->type = JSON_TYPE_NUMBER;
193 token->decimal = atof(json->pos);
197 Parse_Error(json, PARSE_ERR_INVAL, "a valid number");
201 Parse_Next(json, (lookahead - json->pos) - 1);
203 return Json_Parse_Terminator(json, parent, NULL);
206 static const char *keyword_list[] =
215 static inline qjson_token_t *Json_Parse_Keyword(struct qparser_state_s *json, qjson_token_t *parent, qjson_token_t *token)
219 for (int i = 0; keyword_list[i]; i++)
221 keyword_size = strlen(keyword_list[i]);
223 if(!strncmp(keyword_list[i], json->pos, keyword_size))
225 // Don't advance the entire length of the keyword or we might run into a valid token that'd go missed.
226 Parse_Next(json, keyword_size - 1);
228 token->type = JSON_TYPE_NULL;
231 token->type = JSON_TYPE_BOOL;
235 return Json_Parse_Terminator(json, parent, NULL);
238 Parse_Error(json, PARSE_ERR_INVAL, "true, false, or null");
242 static inline qjson_token_t *Json_Parse_Value(qparser_state_t *json, qjson_token_t *parent, qjson_token_t *token)
244 switch(Parse_NextToken(json, 0))
247 return Json_Parse_String(json, parent, token);
249 return Json_Parse_Object(json, parent, token);
251 return Json_Parse_Array(json, parent, token);
253 return Json_Parse_Number(json, parent, token);
257 return Json_Parse_Keyword(json, parent, token);
259 if(isdigit(*json->pos))
260 return Json_Parse_Number(json, parent, token);
262 Parse_Error(json, PARSE_ERR_INVAL, "a value");
266 static inline qjson_token_t *Json_Parse_Single(qparser_state_t *json, qjson_token_t *parent, qjson_token_t *token)
268 // TODO: Handle blank arrays
270 token = Json_Parse_NewToken(json, parent);
271 return Json_Parse_Value(json, parent, token);
274 static inline qjson_token_t *Json_Parse_Pair(struct qparser_state_s *json, qjson_token_t *parent, qjson_token_t *token)
279 Parse_NextToken(json, 0);
281 // TODO: Handle blank objects
283 start = &json->pos[1];
285 while(json->pos[1] != '"')
288 if(ISWHITESPACE(*json->pos))
290 Parse_Error(json, PARSE_ERR_INVAL, "a key without whitespace");
298 Parse_Error(json, PARSE_ERR_INVAL, "a key");
302 if(Parse_NextToken(json, 1) != ':')
304 Parse_Error(json, PARSE_ERR_INVAL, "':'");
308 token = Json_Parse_NewToken(json, parent);
309 token->key = (char *)Z_Malloc(length + 1);
310 memcpy(token->key, start, length);
312 return Json_Parse_Value(json, parent, token);
315 static inline qjson_token_t *Json_Parse_Terminator(qparser_state_t *json, qjson_token_t *parent, qjson_token_t *token)
317 switch(Parse_NextToken(json, 0))
323 return Json_Parse_Terminator(json, parent->parent, NULL);
325 if(parent->type == JSON_TYPE_ARRAY)
326 return Json_Parse_Single(json, parent, NULL);
328 return Json_Parse_Pair(json, parent, NULL);
330 Parse_Error(json, PARSE_ERR_INVAL, "']', '}', or ','");
336 static inline qjson_token_t *Json_Parse_Object(struct qparser_state_s *json, qjson_token_t *parent, qjson_token_t *token)
338 //Parse_Indent(json);
341 * Json objects are basically a data map; key-value pairs.
342 * They end in a comma or a closing curly brace.
344 token->type = JSON_TYPE_OBJECT;
345 List_Create(&token->clist);
347 return Json_Parse_Pair(json, token, NULL);
351 static inline qjson_token_t *Json_Parse_Array(struct qparser_state_s *json, qjson_token_t *parent, qjson_token_t *token)
353 //Parse_Indent(json);
356 * Json arrays are basically lists. They can contain
357 * any value, comma-separated, and end with a closing square bracket.
359 token->type = JSON_TYPE_ARRAY;
360 List_Create(&token->clist);
362 return Json_Parse_Single(json, token, NULL);
365 static void Json_Parse_Cleanup(qparser_state_t *json, qjson_token_t *parent, qjson_token_t *token)
367 qjson_token_t *cur, *next;
369 token->type = JSON_TYPE_UNDEFINED;
371 List_For_Each_Entry_Safe(cur, next, &token->clist, list)
373 if(cur->type == JSON_TYPE_ARRAY || cur->type == JSON_TYPE_BOOL)
375 Json_Parse_Cleanup(json, token, cur);
378 List_Delete(&cur->list);
386 Json_Parse_Cleanup(json, parent->parent, parent);
395 // Main function for the parser.
396 static qjson_token_t *Json_Parse_Start(qparser_state_t *json)
398 qjson_token_t *tree = NULL;
399 qjson_token_t *head = NULL;
401 json->callback.CheckComment_SingleLine = Json_Parse_Comment_SingleLine;
402 json->callback.CheckComment_Multiline_Start = Json_Parse_CheckComment_Multiline_Start;
403 json->callback.CheckComment_Multiline_End = Json_Parse_CheckComment_Multiline_End;
405 if(json->buf == NULL)
407 Con_Printf(CON_ERROR "Json_Parse: Empty json file\n");
411 if(setjmp(parse_error))
413 // actually not sure about this
414 Json_Parse_Cleanup(json, NULL, head);
419 head = Json_Parse_NewToken(json, NULL);
421 switch(Parse_NextToken(json, 0))
424 tree = Json_Parse_Object(json, NULL, head);
427 tree = Json_Parse_Array(json, NULL, head);
430 Con_Printf(CON_ERROR "Json_Parse: Not a json file\n");
438 qjson_token_t *Json_Parse_File(const char *file)
440 return Json_Parse_Start(Parse_LoadFile(file));
443 qjson_token_t *Json_Parse(const unsigned char *data)
445 return Json_Parse_Start(Parse_New(data));
448 void Json_Test_f(cmd_state_t *cmd)
450 qjson_token_t *testing = Json_Parse_File("test.json");
452 Con_Printf("hmm yes this json here is made out of json\n");
454 Con_Printf("failure\n");