]> git.xonotic.org Git - xonotic/darkplaces.git/blob - json.c
756a8161709e739a55300668ed83980b66529689
[xonotic/darkplaces.git] / json.c
1 /*
2 Copyright (C) 2021 David Knapp (Cloudwalk)
3
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.
8
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.
12
13 See the GNU General Public License for more details.
14
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.
18
19 */
20
21 #include "darkplaces.h"
22 #include "parser.h"
23
24 typedef enum qjson_type_e
25 {
26         JSON_TYPE_UNDEFINED = 0,
27         JSON_TYPE_OBJECT = 1,
28         JSON_TYPE_ARRAY = 2,
29         JSON_TYPE_STRING = 3,
30         JSON_TYPE_PRIMITIVE = 4
31 } qjson_type_t;
32
33 typedef struct qjson_token_s
34 {
35         qjson_type_t type;
36         struct qjson_token_s *next; // if an array, next will be a NULL terminated array
37         char *string; // ASCII only for now
38 } qjson_token_t;
39
40 typedef struct qjson_state_s
41 {
42         qjson_token_t *head, *cur;
43         qparser_state_t *state;
44 } qjson_state_t;
45
46 static inline void Json_Parse_Object(struct qjson_state_s *state);
47 static inline void Json_Parse_Array(struct qjson_state_s *state);
48
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)
51 {
52         if(*state->pos == '/')
53         {
54                 // FIXME: Let the parser interface increment this?
55                 if(*state->pos++ == '/')
56                         return true;
57                 else
58                         Parse_Error(state, PARSE_ERR_INVAL, "// or /*");
59         }
60         return false;
61 }
62
63 static qbool Json_Parse_CheckComment_Multiline_Start(struct qparser_state_s *state)
64 {
65         if(*state->pos == '/')
66         {
67                 // FIXME: Let the parser interface increment this?
68                 if(*state->pos++ == '*')
69                         return true;
70                 else
71                         Parse_Error(state, PARSE_ERR_INVAL, "// or /*");
72         }
73         return false;
74 }
75
76 static qbool Json_Parse_CheckComment_Multiline_End(struct qparser_state_s *state)
77 {
78         if(*state->pos == '*')
79         {
80                 // FIXME: Let the parser interface increment this?
81                 if(*state->pos++ == '/')
82                         return true;
83         }
84         return false;
85 }
86
87 static inline qbool Json_Handle_String_Escape(struct qjson_state_s *json)
88 {
89         switch(*json->state->pos)
90         {
91         case '\\':
92         case '/':
93         case 'b':
94         case 'f':
95         case 'n':
96         case 'r':
97         case 't':
98         case 'u':
99                 return true; // TODO
100         default:
101                 return false;
102         }
103 }
104
105 // TODO: handle escape sequences
106 static inline void Json_Parse_String(struct qjson_state_s *json)
107 {
108         do {
109                 Parse_Next(json->state, 1);
110                 if(*json->state->pos == '\\')
111                 {
112                         Parse_Next(json->state, 1);
113                         if(Json_Handle_String_Escape(json))
114                                 continue;
115                         Parse_Error(json->state, PARSE_ERR_INVAL, "a valid escape sequence");
116                 }
117         } while(*json->state->pos != '"');
118 }
119
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)
122 {
123         int i, numsize;
124         const unsigned char *in = json->state->pos;
125         //char out[128];
126         qbool is_float = false;
127         qbool is_exp = false;
128
129         for(i = 0, numsize = 0; isdigit(in[i]); i++, numsize++)
130         {
131                 //out[numsize] = in[numsize];
132
133                 if(in[i] == '.')
134                 {
135                         if(is_float || is_exp)
136                                 Parse_Error(json->state, PARSE_ERR_INVAL, "a number");
137                         is_float = true;
138                         i++;
139                         continue;
140                 }
141
142                 if(in[i] == 'e' || in[i] == 'E')
143                 {
144                         if(is_exp)
145                                 Parse_Error(json->state, PARSE_ERR_INVAL, "a number");
146                         if(in[i+1] == '+' || in[i+1] == '-')
147                                 i++;
148                         is_exp = true;
149                         i++;
150                         continue;
151                 }
152         }
153         // TODO: use strtod()
154         Parse_Next(json->state, i);
155         return true;
156 }
157
158 // Parse a keyword.
159 static inline qbool Json_Parse_Keyword(struct qjson_state_s *json, const char *keyword)
160 {
161         size_t keyword_size = strlen(keyword);
162         if(!strncmp(keyword, (const char *)json->state->pos, keyword_size))
163         {
164                 Parse_Next(json->state, keyword_size - 1);
165                 return true;
166         }
167         return false;
168 }
169
170 // Parse a value.
171 static inline qbool Json_Parse_Value(struct qjson_state_s *json)
172 {
173         switch(Parse_NextToken(json->state))
174         {
175         case '"': // string
176                 Json_Parse_String(json);
177                 break;
178         case '{': // object
179                 Json_Parse_Object(json);
180                 break;
181         case '[': // array
182                 Json_Parse_Array(json);
183                 break;
184         case '-':
185                 Json_Parse_Number(json);
186                 break;
187         default:
188                 if(isdigit(*json->state->pos))
189                         Json_Parse_Number(json);
190                 if(Json_Parse_Keyword(json, "true"))
191                         break;
192                 if(Json_Parse_Keyword(json, "false"))
193                         break;
194                 if(Json_Parse_Keyword(json, "null"))
195                         break;
196                 //Parse_Error(json->state, PARSE_ERR_INVAL, "a value");
197                 return false;
198         }
199         return true;
200 }
201
202 static inline qbool Json_Parse_Pairs(struct qjson_state_s *json)
203 {
204         do
205         {
206                 // Parse the key
207                 if(Parse_NextToken(json->state) == '"')
208                 {
209                         Json_Parse_String(json);
210
211                         // And its value
212                         if(Parse_NextToken(json->state) == ':')
213                         {
214                                 if(!Json_Parse_Value(json))
215                                         Parse_Error(json->state, PARSE_ERR_INVAL, "a value");
216                         }
217                         else
218                                 Parse_Error(json->state, PARSE_ERR_INVAL, ":");
219                 }
220                 else
221                         return false;
222         } while (Parse_NextToken(json->state) == ',');
223
224         return true;
225 }
226
227 // Parse an object.
228 static inline void Json_Parse_Object(struct qjson_state_s *json)
229 {
230         Parse_Indent(json->state);
231
232         /*
233          * Json objects are basically a data map; key-value pairs.
234          * They end in a comma or a closing curly brace.
235          */
236         Json_Parse_Pairs(json);
237
238         if(*json->state->pos != '}')
239                 Parse_Error(json->state, PARSE_ERR_INVAL, ", or }");
240
241         Parse_Dedent(json->state);
242 }
243
244 // Parse an array.
245 static inline void Json_Parse_Array(struct qjson_state_s *json)
246 {
247         Parse_Indent(json->state);
248
249         /*
250          * Json arrays are basically lists. They can contain
251          * any value, comma-separated, and end with a closing square bracket.
252          */
253         do {
254                 if(!Json_Parse_Value(json))
255                         break; 
256         } while (Parse_NextToken(json->state) == ',');
257
258         if(*json->state->pos != ']')
259                 Parse_Error(json->state, PARSE_ERR_INVAL, ", or ]");
260
261         Parse_Dedent(json->state);
262 }
263
264 // Main function for the parser.
265 static qjson_token_t *Json_Parse_Main(qjson_state_t *json)
266 {
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;
270
271         if(setjmp(parse_error))
272         {
273                 // actually not sure about this
274                 return NULL;
275         }
276         if(json->state->buf == NULL)
277         {
278                 Con_Printf(CON_ERROR "Json_Parse: Empty json file\n");
279                 return NULL;
280         }
281
282         switch(Parse_NextToken(json->state))
283         {
284         case '{':
285                 Json_Parse_Object(json);
286                 break;
287         case '[':
288                 Json_Parse_Array(json);
289                 break;
290         default:
291                 Con_Printf(CON_ERROR "Json_Parse: Not a json file\n");
292                 return NULL;
293         }
294
295         // Success!
296         // TODO: Actually parse.
297         Con_Printf("Hmm, yes. This json is made of json\n");
298
299         return NULL;
300 }
301
302 qjson_token_t *Json_Parse_File(const char *file)
303 {
304         struct qjson_state_s json =
305         {
306                 .head = NULL,
307                 .cur = NULL,
308                 .state = Parse_LoadFile(file)
309         };
310
311         return Json_Parse_Main(&json);
312 }
313
314 qjson_token_t *Json_Parse(const unsigned char *data)
315 {
316         struct qjson_state_s json =
317         {
318                 .head = NULL,
319                 .cur = NULL,
320                 .state = Parse_New(data)
321         };
322
323         return Json_Parse_Main(&json);
324 }
325
326 void Json_Test_f(cmd_state_t *cmd)
327 {
328         Json_Parse_File("test.json");
329 }