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 // taken from json's wikipedia article
25 const char json_test_string[] =
28 "\t\"firstName\": \"John\",\n"
29 "\t\"lastName\": \"Smith\",\n"
30 "\t\"isAlive\": true,\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"
38 "\t\"phoneNumbers\": [\n"
40 "\t\t\t\"type\": \"home\",\n"
41 "\t\t\t\"number\": \"212 555-1234\"\n"
44 "\t\t\t\"type\": \"office\",\n"
45 "\t\t\t\"number\": \"646 555-4567\"\n"
48 "\t\"children\": [],\n"
49 "\t\"spouse\": null\n"
53 typedef enum qjson_type_e
55 JSON_TYPE_UNDEFINED = 0,
59 JSON_TYPE_PRIMITIVE = 4
62 typedef struct qjson_token_s
65 struct qjson_token_s *next; // if an array, next will be a NULL terminated array
66 char *string; // ASCII only for now
69 typedef struct qjson_state_s
71 qjson_token_t *head, *cur;
72 qparser_state_t state;
75 static void Json_Parse_Object(struct qjson_state_s *state);
76 static void Json_Parse_Array(struct qjson_state_s *state);
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)
81 if(*state->pos == '/')
83 // FIXME: Let the parser interface increment this?
84 if(*state->pos++ == '/')
87 Parse_Error(state, PARSE_ERR_INVAL);
92 static qbool Json_Parse_CheckComment_Multiline_Start(struct qparser_state_s *state)
94 if(*state->pos == '/')
96 // FIXME: Let the parser interface increment this?
97 if(*state->pos++ == '*')
100 Parse_Error(state, PARSE_ERR_INVAL);
105 static qbool Json_Parse_CheckComment_Multiline_End(struct qparser_state_s *state)
107 if(*state->pos == '*')
109 // FIXME: Let the parser interface increment this?
110 if(*state->pos++ == '/')
117 // TODO: handle escape sequences
118 static void Json_Parse_String(struct qjson_state_s *json)
121 Parse_Next(&json->state, 1);
122 if(*json->state.pos == '\\')
124 Parse_Next(&json->state, 1);
127 } while(*json->state.pos != '"');
129 Parse_Next(&json->state, 1);
132 // Handles numbers. Json numbers can be either an integer or a double.
133 static qbool Json_Parse_Number(struct qjson_state_s *json)
136 const char *in = json->state.pos;
138 qbool is_float = false;
139 qbool is_exp = false;
141 for(i = 0, numsize = 0; isdigit(in[i]); i++, numsize++)
143 //out[numsize] = in[numsize];
147 if(is_float || is_exp)
148 Parse_Error(&json->state, PARSE_ERR_INVAL);
154 if(in[i] == 'e' || in[i] == 'E')
157 Parse_Error(&json->state, PARSE_ERR_INVAL);
158 if(in[i+1] == '+' || in[i+1] == '-')
165 // TODO: use strtod()
166 Parse_Next(&json->state, i);
171 static qbool Json_Parse_Keyword(struct qjson_state_s *json, const char *keyword)
173 size_t keyword_size = strlen(keyword);
174 if(!strncmp(keyword, json->state.pos, keyword_size))
176 Parse_Next(&json->state, keyword_size);
183 static void Json_Parse_Value(struct qjson_state_s *json)
185 Parse_Next(&json->state, 1);
187 switch(Parse_NextToken(&json->state))
190 Json_Parse_String(json);
193 Json_Parse_Object(json);
196 Json_Parse_Array(json);
199 Json_Parse_Number(json);
202 if(Json_Parse_Keyword(json, "true"))
204 if(Json_Parse_Keyword(json, "false"))
206 if(Json_Parse_Keyword(json, "null"))
208 if(isdigit(*json->state.pos))
209 Json_Parse_Number(json);
214 static void Json_Parse_Object(struct qjson_state_s *json)
217 * Json objects are basically a data map; key-value pairs.
218 * They end in a comma or a closing curly brace.
221 Parse_Next(&json->state, 1);
224 if(Parse_NextToken(&json->state) == '"')
225 Json_Parse_String(json);
230 if(Parse_NextToken(&json->state) == ':')
231 Json_Parse_Value(json);
234 } while (Parse_NextToken(&json->state) == ',');
236 if(Parse_NextToken(&json->state) == '}')
239 Parse_Error(&json->state, PARSE_ERR_INVAL);
243 static void Json_Parse_Array(struct qjson_state_s *json)
246 * Json arrays are basically lists. They can contain
247 * any value, comma-separated, and end with a closing square bracket.
250 Json_Parse_Value(json);
251 } while (Parse_NextToken(&json->state) == ',');
253 if(Parse_NextToken(&json->state) == ']')
256 Parse_Error(&json->state, PARSE_ERR_INVAL);
259 // Main function for the parser.
260 qjson_token_t *Json_Parse(const char *data)
262 struct qjson_state_s json =
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
284 Con_Printf(CON_ERROR "Json_Parse: Empty json file\n");
288 if(setjmp(parse_error))
290 // actually not sure about this
294 if(Parse_NextToken(&(json.state)) == '{')
295 Json_Parse_Object(&json);
298 Con_Printf(CON_ERROR "Json_Parse: Not a json file\n");
303 // TODO: Actually parse.
304 Con_Printf("Hmm, yes. This json is made of json\n");
309 void Json_Test_f(cmd_state_t *cmd)
311 Json_Parse(json_test_string);