Serenity Operating System
at master 355 lines 10 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/CharacterTypes.h> 8#include <AK/FloatingPointStringConversions.h> 9#include <AK/JsonArray.h> 10#include <AK/JsonObject.h> 11#include <AK/JsonParser.h> 12#include <math.h> 13 14namespace AK { 15 16constexpr bool is_space(int ch) 17{ 18 return ch == '\t' || ch == '\n' || ch == '\r' || ch == ' '; 19} 20 21ErrorOr<DeprecatedString> JsonParser::consume_and_unescape_string() 22{ 23 if (!consume_specific('"')) 24 return Error::from_string_literal("JsonParser: Expected '\"'"); 25 StringBuilder final_sb; 26 27 for (;;) { 28 size_t peek_index = m_index; 29 char ch = 0; 30 for (;;) { 31 if (peek_index == m_input.length()) 32 break; 33 ch = m_input[peek_index]; 34 if (ch == '"' || ch == '\\') 35 break; 36 if (is_ascii_c0_control(ch)) 37 return Error::from_string_literal("JsonParser: Error while parsing string"); 38 ++peek_index; 39 } 40 41 while (peek_index != m_index) { 42 final_sb.append(m_input[m_index]); 43 m_index++; 44 } 45 46 if (m_index == m_input.length()) 47 break; 48 if (ch == '"') 49 break; 50 if (ch != '\\') { 51 final_sb.append(consume()); 52 continue; 53 } 54 ignore(); 55 if (next_is('"')) { 56 ignore(); 57 final_sb.append('"'); 58 continue; 59 } 60 61 if (next_is('\\')) { 62 ignore(); 63 final_sb.append('\\'); 64 continue; 65 } 66 67 if (next_is('/')) { 68 ignore(); 69 final_sb.append('/'); 70 continue; 71 } 72 73 if (next_is('n')) { 74 ignore(); 75 final_sb.append('\n'); 76 continue; 77 } 78 79 if (next_is('r')) { 80 ignore(); 81 final_sb.append('\r'); 82 continue; 83 } 84 85 if (next_is('t')) { 86 ignore(); 87 final_sb.append('\t'); 88 continue; 89 } 90 91 if (next_is('b')) { 92 ignore(); 93 final_sb.append('\b'); 94 continue; 95 } 96 97 if (next_is('f')) { 98 ignore(); 99 final_sb.append('\f'); 100 continue; 101 } 102 103 if (next_is('u')) { 104 ignore(); 105 if (tell_remaining() < 4) 106 return Error::from_string_literal("JsonParser: EOF while parsing Unicode escape"); 107 108 auto code_point = AK::StringUtils::convert_to_uint_from_hex(consume(4)); 109 if (code_point.has_value()) { 110 final_sb.append_code_point(code_point.value()); 111 continue; 112 } 113 return Error::from_string_literal("JsonParser: Error while parsing Unicode escape"); 114 } 115 116 return Error::from_string_literal("JsonParser: Error while parsing string"); 117 } 118 if (!consume_specific('"')) 119 return Error::from_string_literal("JsonParser: Expected '\"'"); 120 121 return final_sb.to_deprecated_string(); 122} 123 124ErrorOr<JsonValue> JsonParser::parse_object() 125{ 126 JsonObject object; 127 if (!consume_specific('{')) 128 return Error::from_string_literal("JsonParser: Expected '{'"); 129 for (;;) { 130 ignore_while(is_space); 131 if (peek() == '}') 132 break; 133 ignore_while(is_space); 134 auto name = TRY(consume_and_unescape_string()); 135 if (name.is_null()) 136 return Error::from_string_literal("JsonParser: Expected object property name"); 137 ignore_while(is_space); 138 if (!consume_specific(':')) 139 return Error::from_string_literal("JsonParser: Expected ':'"); 140 ignore_while(is_space); 141 auto value = TRY(parse_helper()); 142 object.set(name, move(value)); 143 ignore_while(is_space); 144 if (peek() == '}') 145 break; 146 if (!consume_specific(',')) 147 return Error::from_string_literal("JsonParser: Expected ','"); 148 ignore_while(is_space); 149 if (peek() == '}') 150 return Error::from_string_literal("JsonParser: Unexpected '}'"); 151 } 152 if (!consume_specific('}')) 153 return Error::from_string_literal("JsonParser: Expected '}'"); 154 return JsonValue { move(object) }; 155} 156 157ErrorOr<JsonValue> JsonParser::parse_array() 158{ 159 JsonArray array; 160 if (!consume_specific('[')) 161 return Error::from_string_literal("JsonParser: Expected '['"); 162 for (;;) { 163 ignore_while(is_space); 164 if (peek() == ']') 165 break; 166 auto element = TRY(parse_helper()); 167 array.append(move(element)); 168 ignore_while(is_space); 169 if (peek() == ']') 170 break; 171 if (!consume_specific(',')) 172 return Error::from_string_literal("JsonParser: Expected ','"); 173 ignore_while(is_space); 174 if (peek() == ']') 175 return Error::from_string_literal("JsonParser: Unexpected ']'"); 176 } 177 ignore_while(is_space); 178 if (!consume_specific(']')) 179 return Error::from_string_literal("JsonParser: Expected ']'"); 180 return JsonValue { move(array) }; 181} 182 183ErrorOr<JsonValue> JsonParser::parse_string() 184{ 185 auto string = TRY(consume_and_unescape_string()); 186 return JsonValue(move(string)); 187} 188 189ErrorOr<JsonValue> JsonParser::parse_number() 190{ 191 Vector<char, 32> number_buffer; 192 193 auto start_index = tell(); 194 195 bool negative = false; 196 if (peek() == '-') { 197 number_buffer.append('-'); 198 ++m_index; 199 negative = true; 200 201 if (!is_ascii_digit(peek())) 202 return Error::from_string_literal("JsonParser: Unexpected '-' without further digits"); 203 } 204 205 auto fallback_to_double_parse = [&]() -> ErrorOr<JsonValue> { 206#ifdef KERNEL 207# error JSONParser is currently not available for the Kernel because it disallows floating point. \ 208 If you want to make this KERNEL compatible you can just make this fallback_to_double \ 209 function fail with an error in KERNEL mode. 210#endif 211 // FIXME: Since we know all the characters so far are ascii digits (and one . or e) we could 212 // use that in the floating point parser. 213 214 // The first part should be just ascii digits 215 StringView view = m_input.substring_view(start_index); 216 217 char const* start = view.characters_without_null_termination(); 218 auto parse_result = parse_first_floating_point(start, start + view.length()); 219 220 if (parse_result.parsed_value()) { 221 auto characters_parsed = parse_result.end_ptr - start; 222 m_index = start_index + characters_parsed; 223 224 return JsonValue(parse_result.value); 225 } 226 return Error::from_string_literal("JsonParser: Invalid floating point"); 227 }; 228 229 if (peek() == '0') { 230 if (is_ascii_digit(peek(1))) 231 return Error::from_string_literal("JsonParser: Cannot have leading zeros"); 232 233 // Leading zeros are not allowed, however we can have a '.' or 'e' with 234 // valid digits after just a zero. These cases will be detected by having the next element 235 // start with a '.' or 'e'. 236 } 237 238 bool all_zero = true; 239 for (;;) { 240 char ch = peek(); 241 if (ch == '.') { 242 if (!is_ascii_digit(peek(1))) 243 return Error::from_string_literal("JsonParser: Must have digits after decimal point"); 244 245 return fallback_to_double_parse(); 246 } 247 if (ch == 'e' || ch == 'E') { 248 char next = peek(1); 249 if (!is_ascii_digit(next) && ((next != '+' && next != '-') || !is_ascii_digit(peek(2)))) 250 return Error::from_string_literal("JsonParser: Must have digits after exponent with an optional sign inbetween"); 251 252 return fallback_to_double_parse(); 253 } 254 255 if (is_ascii_digit(ch)) { 256 if (ch != '0') 257 all_zero = false; 258 259 number_buffer.append(ch); 260 ++m_index; 261 continue; 262 } 263 264 break; 265 } 266 267 // Negative zero is always a double 268 if (negative && all_zero) 269 return JsonValue(-0.0); 270 271 StringView number_string(number_buffer.data(), number_buffer.size()); 272 273 auto to_unsigned_result = number_string.to_uint<u64>(); 274 if (to_unsigned_result.has_value()) { 275 if (*to_unsigned_result <= NumericLimits<u32>::max()) 276 return JsonValue((u32)*to_unsigned_result); 277 278 return JsonValue(*to_unsigned_result); 279 } else if (auto signed_number = number_string.to_int<i64>(); signed_number.has_value()) { 280 281 if (*signed_number <= NumericLimits<i32>::max()) 282 return JsonValue((i32)*signed_number); 283 284 return JsonValue(*signed_number); 285 } 286 287 // It's possible the unsigned value is bigger than u64 max 288 return fallback_to_double_parse(); 289} 290 291ErrorOr<JsonValue> JsonParser::parse_true() 292{ 293 if (!consume_specific("true")) 294 return Error::from_string_literal("JsonParser: Expected 'true'"); 295 return JsonValue(true); 296} 297 298ErrorOr<JsonValue> JsonParser::parse_false() 299{ 300 if (!consume_specific("false")) 301 return Error::from_string_literal("JsonParser: Expected 'false'"); 302 return JsonValue(false); 303} 304 305ErrorOr<JsonValue> JsonParser::parse_null() 306{ 307 if (!consume_specific("null")) 308 return Error::from_string_literal("JsonParser: Expected 'null'"); 309 return JsonValue(JsonValue::Type::Null); 310} 311 312ErrorOr<JsonValue> JsonParser::parse_helper() 313{ 314 ignore_while(is_space); 315 auto type_hint = peek(); 316 switch (type_hint) { 317 case '{': 318 return parse_object(); 319 case '[': 320 return parse_array(); 321 case '"': 322 return parse_string(); 323 case '-': 324 case '0': 325 case '1': 326 case '2': 327 case '3': 328 case '4': 329 case '5': 330 case '6': 331 case '7': 332 case '8': 333 case '9': 334 return parse_number(); 335 case 'f': 336 return parse_false(); 337 case 't': 338 return parse_true(); 339 case 'n': 340 return parse_null(); 341 } 342 343 return Error::from_string_literal("JsonParser: Unexpected character"); 344} 345 346ErrorOr<JsonValue> JsonParser::parse() 347{ 348 auto result = TRY(parse_helper()); 349 ignore_while(is_space); 350 if (!is_eof()) 351 return Error::from_string_literal("JsonParser: Didn't consume all input"); 352 return result; 353} 354 355}