]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - json.c
Move general purpose parsing functions to a new helper library
[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 // taken from json's wikipedia article
25 const char json_test_string[] =
26 {
27         "{\n"
28         "\t\"firstName\": \"John\",\n"
29         "\t\"lastName\": \"Smith\",\n"
30         "\t\"isAlive\": true,\n"
31         "\t\"age\": 27,\n"
32         "\t\"address\": {\n"
33         "\t\t\"streetAddress\": \"21 2nd Street\",\n"
34         "\t\t\"city\": \"New York\",\n"
35         "\t\t\"state\": \"NY\",\n"
36         "\t\t\"postalCode\": \"10021-3100\"\n"
37         "\t},\n"
38         "\t\"phoneNumbers\": [\n"
39         "\t\t{\n"
40         "\t\t\t\"type\": \"home\",\n"
41         "\t\t\t\"number\": \"212 555-1234\"\n"
42         "\t\t},\n"
43         "\t\t{\n"
44         "\t\t\t\"type\": \"office\",\n"
45         "\t\t\t\"number\": \"646 555-4567\"\n"
46         "\t\t}\n"
47         "\t],\n"
48         "\t\"children\": [],\n"
49         "\t\"spouse\": null\n"
50         "}\n\000"
51 };
52
53 typedef enum qjson_type_e
54 {
55         JSON_TYPE_UNDEFINED = 0,
56         JSON_TYPE_OBJECT = 1,
57         JSON_TYPE_ARRAY = 2,
58         JSON_TYPE_STRING = 3,
59         JSON_TYPE_PRIMITIVE = 4
60 } qjson_type_t;
61
62 typedef struct qjson_token_s
63 {
64         qjson_type_t type;
65         struct qjson_token_s *next; // if an array, next will be a NULL terminated array
66         char *string; // ASCII only for now
67 } qjson_token_t;
68
69 typedef struct qjson_state_s
70 {
71         qjson_token_t *head, *cur;
72         qparser_state_t state;
73 } qjson_state_t;
74
75 static void Json_Parse_Object(struct qjson_state_s *state);
76 static void Json_Parse_Array(struct qjson_state_s *state);
77
78 // Checks for C/C++-style comments and ignores them. This is not standard json.
79 static qbool Json_Parse_Comment_SingleLine(struct qparser_state_s *state)
80 {
81         if(*state->pos == '/')
82         {
83                 // FIXME: Let the parser interface increment this?
84                 if(*state->pos++ == '/')
85                         return true;
86                 else
87                         Parse_Error(state, PARSE_ERR_INVAL);
88         }
89         return false;
90 }
91
92 static qbool Json_Parse_CheckComment_Multiline_Start(struct qparser_state_s *state)
93 {
94         if(*state->pos == '/')
95         {
96                 // FIXME: Let the parser interface increment this?
97                 if(*state->pos++ == '*')
98                         return true;
99                 else
100                         Parse_Error(state, PARSE_ERR_INVAL);
101         }
102         return false;
103 }
104
105 static qbool Json_Parse_CheckComment_Multiline_End(struct qparser_state_s *state)
106 {
107         if(*state->pos == '*')
108         {
109                 // FIXME: Let the parser interface increment this?
110                 if(*state->pos++ == '/')
111                         return true;
112         }
113         return false;
114 }
115
116
117 // TODO: handle escape sequences
118 static void Json_Parse_String(struct qjson_state_s *json)
119 {
120         do {
121                 Parse_Next(&json->state, 1);
122                 if(*json->state.pos == '\\')
123                 {
124                         Parse_Next(&json->state, 1);
125                         continue;
126                 }
127         } while(*json->state.pos != '"');
128
129         Parse_Next(&json->state, 1);
130 }
131
132 // Handles numbers. Json numbers can be either an integer or a double.
133 static qbool Json_Parse_Number(struct qjson_state_s *json)
134 {
135         int i, numsize;
136         const char *in = json->state.pos;
137         //char out[128];
138         qbool is_float = false;
139         qbool is_exp = false;
140
141         for(i = 0, numsize = 0; isdigit(in[i]); i++, numsize++)
142         {
143                 //out[numsize] = in[numsize];
144
145                 if(in[i] == '.')
146                 {
147                         if(is_float || is_exp)
148                                 Parse_Error(&json->state, PARSE_ERR_INVAL);
149                         is_float = true;
150                         i++;
151                         continue;
152                 }
153
154                 if(in[i] == 'e' || in[i] == 'E')
155                 {
156                         if(is_exp)
157                                 Parse_Error(&json->state, PARSE_ERR_INVAL);
158                         if(in[i+1] == '+' || in[i+1] == '-')
159                                 i++;
160                         is_exp = true;
161                         i++;
162                         continue;
163                 }
164         }
165         // TODO: use strtod()
166         Parse_Next(&json->state, i);
167         return true;
168 }
169
170 // Parse a keyword.
171 static qbool Json_Parse_Keyword(struct qjson_state_s *json, const char *keyword)
172 {
173         size_t keyword_size = strlen(keyword);
174         if(!strncmp(keyword, json->state.pos, keyword_size))
175         {
176                 Parse_Next(&json->state, keyword_size);
177                 return true;
178         }
179         return false;
180 }
181
182 // Parse a value.
183 static void Json_Parse_Value(struct qjson_state_s *json)
184 {
185         Parse_Next(&json->state, 1);
186
187         switch(Parse_NextToken(&json->state))
188         {
189         case '"': // string
190                 Json_Parse_String(json);
191                 break;
192         case '{': // object
193                 Json_Parse_Object(json);
194                 break;
195         case '[': // array
196                 Json_Parse_Array(json);
197                 break;
198         case '-':
199                 Json_Parse_Number(json);
200                 break;
201         default:
202                 if(Json_Parse_Keyword(json, "true"))
203                         break;
204                 if(Json_Parse_Keyword(json, "false"))
205                         break;
206                 if(Json_Parse_Keyword(json, "null"))
207                         break;
208                 if(isdigit(*json->state.pos))
209                         Json_Parse_Number(json);
210         }
211 }
212
213 // Parse an object.
214 static void Json_Parse_Object(struct qjson_state_s *json)
215 {
216         /*
217          * Json objects are basically a data map; key-value pairs.
218          * They end in a comma or a closing curly brace.
219          */
220         do {
221                 Parse_Next(&json->state, 1);
222
223                 // Parse the key
224                 if(Parse_NextToken(&json->state) == '"')
225                         Json_Parse_String(json);
226                 else
227                         goto fail;
228                 
229                 // And its value
230                 if(Parse_NextToken(&json->state) == ':')
231                         Json_Parse_Value(json);
232                 else
233                         goto fail;
234         } while (Parse_NextToken(&json->state) == ',');
235
236         if(Parse_NextToken(&json->state) == '}')
237                 return;
238 fail:
239         Parse_Error(&json->state, PARSE_ERR_INVAL);
240 }
241
242 // Parse an array.
243 static void Json_Parse_Array(struct qjson_state_s *json)
244 {
245         /*
246          * Json arrays are basically lists. They can contain
247          * any value, comma-separated, and end with a closing square bracket.
248          */
249         do {
250                 Json_Parse_Value(json);
251         } while (Parse_NextToken(&json->state) == ',');
252
253         if(Parse_NextToken(&json->state) == ']')
254                 return;
255         else
256                 Parse_Error(&json->state, PARSE_ERR_INVAL);
257 }
258
259 // Main function for the parser.
260 qjson_token_t *Json_Parse(const char *data)
261 {
262         struct qjson_state_s json =
263         {
264                 .head = NULL,
265                 .cur = NULL,
266                 .state =
267                 {
268                         .name = "json",
269                         .buf = data,
270                         .pos = &data[0],
271                         .line = 1,
272                         .col = 1,
273                         .callback =
274                         {
275                                 .CheckComment_SingleLine = Json_Parse_Comment_SingleLine,
276                                 .CheckComment_Multiline_Start = Json_Parse_CheckComment_Multiline_Start,
277                                 .CheckComment_Multiline_End = Json_Parse_CheckComment_Multiline_End
278                         }
279                 }
280         };
281
282         if(data == NULL)
283         {
284                 Con_Printf(CON_ERROR "Json_Parse: Empty json file\n");
285                 return NULL;
286         }
287
288         if(setjmp(parse_error))
289         {
290                 // actually not sure about this
291                 return NULL;
292         }
293
294         if(Parse_NextToken(&(json.state)) == '{')
295                 Json_Parse_Object(&json);
296         else
297         {
298                 Con_Printf(CON_ERROR "Json_Parse: Not a json file\n");
299                 return NULL;
300         }
301
302         // Success!
303         // TODO: Actually parse.
304         Con_Printf("Hmm, yes. This json is made of json\n");
305
306         return NULL;
307 }
308
309 void Json_Test_f(cmd_state_t *cmd)
310 {
311         Json_Parse(json_test_string);
312 }