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_PRIMITIVE = 4
33 typedef struct qjson_token_s
36 struct qjson_token_s *next; // if an array, next will be a NULL terminated array
37 char *string; // ASCII only for now
40 typedef struct qjson_state_s
42 qjson_token_t *head, *cur;
43 qparser_state_t *state;
46 static inline void Json_Parse_Object(struct qjson_state_s *state);
47 static inline void Json_Parse_Array(struct qjson_state_s *state);
49 // Checks for C/C++-style comments and ignores them. This is not standard json.
50 static qbool Json_Parse_Comment_SingleLine(struct qparser_state_s *state)
52 if(*state->pos == '/')
54 // FIXME: Let the parser interface increment this?
55 if(*state->pos++ == '/')
58 Parse_Error(state, PARSE_ERR_INVAL, "// or /*");
63 static qbool Json_Parse_CheckComment_Multiline_Start(struct qparser_state_s *state)
65 if(*state->pos == '/')
67 // FIXME: Let the parser interface increment this?
68 if(*state->pos++ == '*')
71 Parse_Error(state, PARSE_ERR_INVAL, "// or /*");
76 static qbool Json_Parse_CheckComment_Multiline_End(struct qparser_state_s *state)
78 if(*state->pos == '*')
80 // FIXME: Let the parser interface increment this?
81 if(*state->pos++ == '/')
87 static inline qbool Json_Handle_String_Escape(struct qjson_state_s *json)
89 switch(*json->state->pos)
105 // TODO: handle escape sequences
106 static inline void Json_Parse_String(struct qjson_state_s *json)
109 Parse_Next(json->state, 1);
110 if(*json->state->pos == '\\')
112 Parse_Next(json->state, 1);
113 if(Json_Handle_String_Escape(json))
115 Parse_Error(json->state, PARSE_ERR_INVAL, "a valid escape sequence");
117 } while(*json->state->pos != '"');
120 // Handles numbers. Json numbers can be either an integer or a double.
121 static inline qbool Json_Parse_Number(struct qjson_state_s *json)
124 const unsigned char *in = json->state->pos;
126 qbool is_float = false;
127 qbool is_exp = false;
129 for(i = 0, numsize = 0; isdigit(in[i]); i++, numsize++)
131 //out[numsize] = in[numsize];
135 if(is_float || is_exp)
136 Parse_Error(json->state, PARSE_ERR_INVAL, "a number");
142 if(in[i] == 'e' || in[i] == 'E')
145 Parse_Error(json->state, PARSE_ERR_INVAL, "a number");
146 if(in[i+1] == '+' || in[i+1] == '-')
153 // TODO: use strtod()
154 Parse_Next(json->state, i);
159 static inline qbool Json_Parse_Keyword(struct qjson_state_s *json, const char *keyword)
161 size_t keyword_size = strlen(keyword);
162 if(!strncmp(keyword, (const char *)json->state->pos, keyword_size))
164 Parse_Next(json->state, keyword_size - 1);
171 static inline qbool Json_Parse_Value(struct qjson_state_s *json)
173 switch(Parse_NextToken(json->state))
176 Json_Parse_String(json);
179 Json_Parse_Object(json);
182 Json_Parse_Array(json);
185 Json_Parse_Number(json);
188 if(isdigit(*json->state->pos))
189 Json_Parse_Number(json);
190 if(Json_Parse_Keyword(json, "true"))
192 if(Json_Parse_Keyword(json, "false"))
194 if(Json_Parse_Keyword(json, "null"))
196 //Parse_Error(json->state, PARSE_ERR_INVAL, "a value");
202 static inline qbool Json_Parse_Pairs(struct qjson_state_s *json)
207 if(Parse_NextToken(json->state) == '"')
209 Json_Parse_String(json);
212 if(Parse_NextToken(json->state) == ':')
214 if(!Json_Parse_Value(json))
215 Parse_Error(json->state, PARSE_ERR_INVAL, "a value");
218 Parse_Error(json->state, PARSE_ERR_INVAL, ":");
222 } while (Parse_NextToken(json->state) == ',');
228 static inline void Json_Parse_Object(struct qjson_state_s *json)
230 Parse_Indent(json->state);
233 * Json objects are basically a data map; key-value pairs.
234 * They end in a comma or a closing curly brace.
236 Json_Parse_Pairs(json);
238 if(*json->state->pos != '}')
239 Parse_Error(json->state, PARSE_ERR_INVAL, ", or }");
241 Parse_Dedent(json->state);
245 static inline void Json_Parse_Array(struct qjson_state_s *json)
247 Parse_Indent(json->state);
250 * Json arrays are basically lists. They can contain
251 * any value, comma-separated, and end with a closing square bracket.
254 if(!Json_Parse_Value(json))
256 } while (Parse_NextToken(json->state) == ',');
258 if(*json->state->pos != ']')
259 Parse_Error(json->state, PARSE_ERR_INVAL, ", or ]");
261 Parse_Dedent(json->state);
264 // Main function for the parser.
265 static qjson_token_t *Json_Parse_Main(qjson_state_t *json)
267 json->state->callback.CheckComment_SingleLine = Json_Parse_Comment_SingleLine;
268 json->state->callback.CheckComment_Multiline_Start = Json_Parse_CheckComment_Multiline_Start;
269 json->state->callback.CheckComment_Multiline_End = Json_Parse_CheckComment_Multiline_End;
271 if(setjmp(parse_error))
273 // actually not sure about this
276 if(json->state->buf == NULL)
278 Con_Printf(CON_ERROR "Json_Parse: Empty json file\n");
282 switch(Parse_NextToken(json->state))
285 Json_Parse_Object(json);
288 Json_Parse_Array(json);
291 Con_Printf(CON_ERROR "Json_Parse: Not a json file\n");
296 // TODO: Actually parse.
297 Con_Printf("Hmm, yes. This json is made of json\n");
302 qjson_token_t *Json_Parse_File(const char *file)
304 struct qjson_state_s json =
308 .state = Parse_LoadFile(file)
311 return Json_Parse_Main(&json);
314 qjson_token_t *Json_Parse(const unsigned char *data)
316 struct qjson_state_s json =
320 .state = Parse_New(data)
323 return Json_Parse_Main(&json);
326 void Json_Test_f(cmd_state_t *cmd)
328 Json_Parse_File("test.json");