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,
34 typedef struct qjson_token_s
38 struct qjson_token_s *prev, *next; // Linked list for arrays
39 struct qjson_token_s *parent, *child;
50 typedef struct qjson_state_s
52 qjson_token_t *head, *cur;
53 qparser_state_t *state;
56 static inline void Json_Parse_Object(struct qjson_state_s *state);
57 static inline void Json_Parse_Array(struct qjson_state_s *state);
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 qbool Json_Handle_String_Escape(struct qjson_state_s *json)
99 switch(*json->state->pos)
115 // TODO: handle escape sequences
116 static inline void Json_Parse_String(struct qjson_state_s *json)
119 Parse_Next(json->state, 1);
120 if(*json->state->pos == '\\')
122 Parse_Next(json->state, 1);
123 if(Json_Handle_String_Escape(json))
125 Parse_Error(json->state, PARSE_ERR_INVAL, "a valid escape sequence");
127 } while(*json->state->pos != '"');
130 // Handles numbers. Json numbers can be either an integer or a double.
131 static inline qbool Json_Parse_Number(struct qjson_state_s *json)
134 const char *in = json->state->pos;
136 qbool is_float = false;
137 qbool is_exp = false;
139 for(i = 0, numsize = 0; isdigit(in[i]); i++, numsize++)
141 //out[numsize] = in[numsize];
145 if(is_float || is_exp)
146 Parse_Error(json->state, PARSE_ERR_INVAL, "a number");
152 if(in[i] == 'e' || in[i] == 'E')
155 Parse_Error(json->state, PARSE_ERR_INVAL, "a number");
156 if(in[i+1] == '+' || in[i+1] == '-')
163 // TODO: use strtod()
164 Parse_Next(json->state, i - 1);
168 static const char *keyword_list[] =
177 static inline qbool Json_Parse_Keyword(struct qjson_state_s *json)
181 for (int i = 0; keyword_list[i]; i++)
183 keyword_size = strlen(keyword_list[i]);
185 if(!strncmp(keyword_list[i], json->state->pos, keyword_size))
187 // Don't advance the entire length of the keyword or we might run into a valid token that'd go missed.
188 Parse_Next(json->state, keyword_size - 1);
195 static inline void Json_Parse_Key(struct qjson_state_s *json)
198 Parse_Next(json->state, 1);
199 if(ISWHITESPACE(*json->state->pos))
200 Parse_Error(json->state, PARSE_ERR_INVAL, "a key");
201 } while(*json->state->pos != '"');
205 static inline qbool Json_Parse_Value(struct qjson_state_s *json)
207 switch(Parse_NextToken(json->state))
210 Json_Parse_String(json);
213 Json_Parse_Object(json);
216 Json_Parse_Array(json);
219 Json_Parse_Number(json);
224 Json_Parse_Keyword(json);
227 if(isdigit(*json->state->pos))
229 Json_Parse_Number(json);
232 //Parse_Error(json->state, PARSE_ERR_INVAL, "a value");
238 static inline qbool Json_Parse_Pairs(struct qjson_state_s *json)
242 if(Parse_NextToken(json->state) == '"')
245 Json_Parse_Key(json);
248 if(Parse_NextToken(json->state) == ':')
250 if(!Json_Parse_Value(json))
251 Parse_Error(json->state, PARSE_ERR_INVAL, "a value");
254 Parse_Error(json->state, PARSE_ERR_INVAL, ":");
258 } while (Parse_NextToken(json->state) == ',');
264 static inline void Json_Parse_Object(struct qjson_state_s *json)
266 Parse_Indent(json->state);
269 * Json objects are basically a data map; key-value pairs.
270 * They end in a comma or a closing curly brace.
272 Json_Parse_Pairs(json);
274 if(*json->state->pos != '}')
275 Parse_Error(json->state, PARSE_ERR_INVAL, ", or }");
277 Parse_Dedent(json->state);
281 static inline void Json_Parse_Array(struct qjson_state_s *json)
283 Parse_Indent(json->state);
286 * Json arrays are basically lists. They can contain
287 * any value, comma-separated, and end with a closing square bracket.
290 if(!Json_Parse_Value(json))
292 } while (Parse_NextToken(json->state) == ',');
294 if(*json->state->pos != ']')
295 Parse_Error(json->state, PARSE_ERR_INVAL, ", or ]");
297 Parse_Dedent(json->state);
300 // Main function for the parser.
301 static qjson_token_t *Json_Parse_Main(qjson_state_t *json)
303 json->state->callback.CheckComment_SingleLine = Json_Parse_Comment_SingleLine;
304 json->state->callback.CheckComment_Multiline_Start = Json_Parse_CheckComment_Multiline_Start;
305 json->state->callback.CheckComment_Multiline_End = Json_Parse_CheckComment_Multiline_End;
307 if(setjmp(parse_error))
309 // actually not sure about this
312 if(json->state->buf == NULL)
314 Con_Printf(CON_ERROR "Json_Parse: Empty json file\n");
318 switch(Parse_NextToken(json->state))
321 Json_Parse_Object(json);
324 Json_Parse_Array(json);
327 Con_Printf(CON_ERROR "Json_Parse: Not a json file\n");
332 // TODO: Actually parse.
333 Con_Printf("Hmm, yes. This json is made of json\n");
338 qjson_token_t *Json_Parse_File(const char *file)
340 struct qjson_state_s json =
344 .state = Parse_LoadFile(file)
347 return Json_Parse_Main(&json);
350 qjson_token_t *Json_Parse(const unsigned char *data)
352 struct qjson_state_s json =
356 .state = Parse_New(data)
359 return Json_Parse_Main(&json);
362 void Json_Test_f(cmd_state_t *cmd)
364 Json_Parse_File("test.json");