]> git.xonotic.org Git - xonotic/darkplaces.git/blob - json.c
Merge branch 'master' into Cloudwalk/json
[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_NUMBER = 4,
31         JSON_TYPE_BOOL = 5
32 } qjson_type_t;
33
34 typedef struct qjson_token_s
35 {
36         qjson_type_t type;
37
38         struct qjson_token_s *prev, *next; // Linked list for arrays
39         struct qjson_token_s *parent, *child;
40         
41         char *key;
42         union
43         {
44                 char *string;
45                 double decimal;
46                 int number;
47         } value;
48 } qjson_token_t;
49
50 typedef struct qjson_state_s
51 {
52         qjson_token_t *head, *cur;
53         qparser_state_t *state;
54 } qjson_state_t;
55
56 static inline void Json_Parse_Object(struct qjson_state_s *state);
57 static inline void Json_Parse_Array(struct qjson_state_s *state);
58
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)
61 {
62         if(*state->pos == '/')
63         {
64                 // FIXME: Let the parser interface increment this?
65                 if(*state->pos++ == '/')
66                         return true;
67                 else
68                         Parse_Error(state, PARSE_ERR_INVAL, "// or /*");
69         }
70         return false;
71 }
72
73 static qbool Json_Parse_CheckComment_Multiline_Start(struct qparser_state_s *state)
74 {
75         if(*state->pos == '/')
76         {
77                 // FIXME: Let the parser interface increment this?
78                 if(*state->pos++ == '*')
79                         return true;
80                 else
81                         Parse_Error(state, PARSE_ERR_INVAL, "// or /*");
82         }
83         return false;
84 }
85
86 static qbool Json_Parse_CheckComment_Multiline_End(struct qparser_state_s *state)
87 {
88         if(*state->pos == '*')
89         {
90                 // FIXME: Let the parser interface increment this?
91                 if(*state->pos++ == '/')
92                         return true;
93         }
94         return false;
95 }
96
97 static inline qbool Json_Handle_String_Escape(struct qjson_state_s *json)
98 {
99         switch(*json->state->pos)
100         {
101         case '\\':
102         case '/':
103         case 'b':
104         case 'f':
105         case 'n':
106         case 'r':
107         case 't':
108         case 'u':
109                 return true; // TODO
110         default:
111                 return false;
112         }
113 }
114
115 // TODO: handle escape sequences
116 static inline void Json_Parse_String(struct qjson_state_s *json)
117 {
118         do {
119                 Parse_Next(json->state, 1);
120                 if(*json->state->pos == '\\')
121                 {
122                         Parse_Next(json->state, 1);
123                         if(Json_Handle_String_Escape(json))
124                                 continue;
125                         Parse_Error(json->state, PARSE_ERR_INVAL, "a valid escape sequence");
126                 }
127         } while(*json->state->pos != '"');
128 }
129
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)
132 {
133         int i, numsize;
134         const char *in = json->state->pos;
135         //char out[128];
136         qbool is_float = false;
137         qbool is_exp = false;
138
139         for(i = 0, numsize = 0; isdigit(in[i]); i++, numsize++)
140         {
141                 //out[numsize] = in[numsize];
142
143                 if(in[i] == '.')
144                 {
145                         if(is_float || is_exp)
146                                 Parse_Error(json->state, PARSE_ERR_INVAL, "a number");
147                         is_float = true;
148                         i++;
149                         continue;
150                 }
151
152                 if(in[i] == 'e' || in[i] == 'E')
153                 {
154                         if(is_exp)
155                                 Parse_Error(json->state, PARSE_ERR_INVAL, "a number");
156                         if(in[i+1] == '+' || in[i+1] == '-')
157                                 i++;
158                         is_exp = true;
159                         i++;
160                         continue;
161                 }
162         }
163         // TODO: use strtod()
164         Parse_Next(json->state, i - 1);
165         return true;
166 }
167
168 static const char *keyword_list[] =
169 {
170         "true",
171         "false",
172         "null",
173         NULL
174 };
175
176 // Parse a keyword.
177 static inline qbool Json_Parse_Keyword(struct qjson_state_s *json)
178 {
179         size_t keyword_size;
180
181         for (int i = 0; keyword_list[i]; i++)
182         {
183                 keyword_size = strlen(keyword_list[i]);
184
185                 if(!strncmp(keyword_list[i], json->state->pos, keyword_size))
186                 {
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);
189                         return true;
190                 }
191         }
192         return false;
193 }
194
195 static inline void Json_Parse_Key(struct qjson_state_s *json)
196 {
197         do {
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 != '"');
202 }
203
204 // Parse a value.
205 static inline qbool Json_Parse_Value(struct qjson_state_s *json)
206 {
207         switch(Parse_NextToken(json->state))
208         {
209         case '"': // string
210                 Json_Parse_String(json);
211                 break;
212         case '{': // object
213                 Json_Parse_Object(json);
214                 break;
215         case '[': // array
216                 Json_Parse_Array(json);
217                 break;
218         case '-':
219                 Json_Parse_Number(json);
220                 break;
221         case 't': // true
222         case 'f': // false
223         case 'n': // null
224                 Json_Parse_Keyword(json);
225                 break;
226         default:
227                 if(isdigit(*json->state->pos))
228                 {
229                         Json_Parse_Number(json);
230                         break;
231                 }
232                 //Parse_Error(json->state, PARSE_ERR_INVAL, "a value");
233                 return false;
234         }
235         return true;
236 }
237
238 static inline qbool Json_Parse_Pairs(struct qjson_state_s *json)
239 {
240         do
241         {
242                 if(Parse_NextToken(json->state) == '"')
243                 {
244                         // Parse the key
245                         Json_Parse_Key(json);
246
247                         // And its value
248                         if(Parse_NextToken(json->state) == ':')
249                         {
250                                 if(!Json_Parse_Value(json))
251                                         Parse_Error(json->state, PARSE_ERR_INVAL, "a value");
252                         }
253                         else
254                                 Parse_Error(json->state, PARSE_ERR_INVAL, ":");
255                 }
256                 else
257                         return false;
258         } while (Parse_NextToken(json->state) == ',');
259
260         return true;
261 }
262
263 // Parse an object.
264 static inline void Json_Parse_Object(struct qjson_state_s *json)
265 {
266         Parse_Indent(json->state);
267
268         /*
269          * Json objects are basically a data map; key-value pairs.
270          * They end in a comma or a closing curly brace.
271          */
272         Json_Parse_Pairs(json);
273
274         if(*json->state->pos != '}')
275                 Parse_Error(json->state, PARSE_ERR_INVAL, ", or }");
276
277         Parse_Dedent(json->state);
278 }
279
280 // Parse an array.
281 static inline void Json_Parse_Array(struct qjson_state_s *json)
282 {
283         Parse_Indent(json->state);
284
285         /*
286          * Json arrays are basically lists. They can contain
287          * any value, comma-separated, and end with a closing square bracket.
288          */
289         do {
290                 if(!Json_Parse_Value(json))
291                         break;
292         } while (Parse_NextToken(json->state) == ',');
293
294         if(*json->state->pos != ']')
295                 Parse_Error(json->state, PARSE_ERR_INVAL, ", or ]");
296
297         Parse_Dedent(json->state);
298 }
299
300 // Main function for the parser.
301 static qjson_token_t *Json_Parse_Main(qjson_state_t *json)
302 {
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;
306
307         if(setjmp(parse_error))
308         {
309                 // actually not sure about this
310                 return NULL;
311         }
312         if(json->state->buf == NULL)
313         {
314                 Con_Printf(CON_ERROR "Json_Parse: Empty json file\n");
315                 return NULL;
316         }
317
318         switch(Parse_NextToken(json->state))
319         {
320         case '{':
321                 Json_Parse_Object(json);
322                 break;
323         case '[':
324                 Json_Parse_Array(json);
325                 break;
326         default:
327                 Con_Printf(CON_ERROR "Json_Parse: Not a json file\n");
328                 return NULL;
329         }
330
331         // Success!
332         // TODO: Actually parse.
333         Con_Printf("Hmm, yes. This json is made of json\n");
334
335         return NULL;
336 }
337
338 qjson_token_t *Json_Parse_File(const char *file)
339 {
340         struct qjson_state_s json =
341         {
342                 .head = NULL,
343                 .cur = NULL,
344                 .state = Parse_LoadFile(file)
345         };
346
347         return Json_Parse_Main(&json);
348 }
349
350 qjson_token_t *Json_Parse(const unsigned char *data)
351 {
352         struct qjson_state_s json =
353         {
354                 .head = NULL,
355                 .cur = NULL,
356                 .state = Parse_New(data)
357         };
358
359         return Json_Parse_Main(&json);
360 }
361
362 void Json_Test_f(cmd_state_t *cmd)
363 {
364         Json_Parse_File("test.json");
365 }