Serenity Operating System
at portability 277 lines 7.1 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/JsonArray.h> 28#include <AK/JsonObject.h> 29#include <AK/JsonParser.h> 30 31namespace AK { 32 33static inline bool is_whitespace(char ch) 34{ 35 return ch == ' ' || ch == '\n' || ch == '\t' || ch == '\v' || ch == '\r'; 36} 37 38char JsonParser::peek() const 39{ 40 if (m_index < m_input.length()) 41 return m_input[m_index]; 42 return '\0'; 43} 44 45char JsonParser::consume() 46{ 47 if (m_index < m_input.length()) 48 return m_input[m_index++]; 49 return '\0'; 50} 51 52template<typename C> 53void JsonParser::consume_while(C condition) 54{ 55 while (condition(peek())) 56 consume(); 57} 58 59void JsonParser::consume_whitespace() 60{ 61 consume_while([](char ch) { return is_whitespace(ch); }); 62} 63 64void JsonParser::consume_specific(char expected_ch) 65{ 66 char consumed_ch = consume(); 67 ASSERT(consumed_ch == expected_ch); 68} 69 70String JsonParser::consume_quoted_string() 71{ 72 consume_specific('"'); 73 Vector<char, 1024> buffer; 74 75 for (;;) { 76 size_t peek_index = m_index; 77 char ch = 0; 78 for (;;) { 79 if (peek_index == m_input.length()) 80 break; 81 ch = m_input[peek_index]; 82 if (ch == '"' || ch == '\\') 83 break; 84 ++peek_index; 85 } 86 87 if (peek_index != m_index) { 88 buffer.append(m_input.characters_without_null_termination() + m_index, peek_index - m_index); 89 m_index = peek_index; 90 } 91 92 if (ch == '"') 93 break; 94 if (ch != '\\') { 95 buffer.append(consume()); 96 continue; 97 } 98 consume(); 99 char escaped_ch = consume(); 100 switch (escaped_ch) { 101 case 'n': 102 case 'r': 103 buffer.append('\n'); 104 break; 105 case 't': 106 buffer.append('\t'); 107 break; 108 case 'b': 109 buffer.append('\b'); 110 break; 111 case 'f': 112 buffer.append('\f'); 113 break; 114 case 'u': 115 consume(); 116 consume(); 117 consume(); 118 consume(); 119 // FIXME: This is obviously not correct, but we don't have non-ASCII support so meh. 120 buffer.append('?'); 121 break; 122 default: 123 buffer.append(escaped_ch); 124 break; 125 } 126 } 127 consume_specific('"'); 128 129 if (buffer.is_empty()) 130 return String::empty(); 131 132 auto& last_string_starting_with_character = m_last_string_starting_with_character[(u8)buffer.first()]; 133 if (last_string_starting_with_character.length() == (size_t)buffer.size()) { 134 if (!memcmp(last_string_starting_with_character.characters(), buffer.data(), buffer.size())) 135 return last_string_starting_with_character; 136 } 137 138 last_string_starting_with_character = String::copy(buffer); 139 return last_string_starting_with_character; 140} 141 142JsonObject JsonParser::parse_object() 143{ 144 JsonObject object; 145 consume_specific('{'); 146 for (;;) { 147 consume_whitespace(); 148 if (peek() == '}') 149 break; 150 consume_whitespace(); 151 auto name = consume_quoted_string(); 152 consume_whitespace(); 153 consume_specific(':'); 154 consume_whitespace(); 155 auto value = parse(); 156 object.set(name, move(value)); 157 consume_whitespace(); 158 if (peek() == '}') 159 break; 160 consume_specific(','); 161 } 162 consume_specific('}'); 163 return object; 164} 165 166JsonArray JsonParser::parse_array() 167{ 168 JsonArray array; 169 consume_specific('['); 170 for (;;) { 171 consume_whitespace(); 172 if (peek() == ']') 173 break; 174 array.append(parse()); 175 consume_whitespace(); 176 if (peek() == ']') 177 break; 178 consume_specific(','); 179 } 180 consume_whitespace(); 181 consume_specific(']'); 182 return array; 183} 184 185JsonValue JsonParser::parse_string() 186{ 187 return consume_quoted_string(); 188} 189 190JsonValue JsonParser::parse_number() 191{ 192 Vector<char, 128> number_buffer; 193 for (;;) { 194 char ch = peek(); 195 if (ch == '-' || (ch >= '0' && ch <= '9')) { 196 number_buffer.append(ch); 197 ++m_index; 198 continue; 199 } 200 break; 201 } 202 203 StringView number_string(number_buffer.data(), number_buffer.size()); 204 bool ok; 205 auto value = JsonValue(number_string.to_uint(ok)); 206 if (!ok) 207 value = JsonValue(number_string.to_int(ok)); 208 ASSERT(ok); 209 return value; 210} 211 212void JsonParser::consume_string(const char* str) 213{ 214 for (size_t i = 0, length = strlen(str); i < length; ++i) 215 consume_specific(str[i]); 216} 217 218JsonValue JsonParser::parse_true() 219{ 220 consume_string("true"); 221 return JsonValue(true); 222} 223 224JsonValue JsonParser::parse_false() 225{ 226 consume_string("false"); 227 return JsonValue(false); 228} 229 230JsonValue JsonParser::parse_null() 231{ 232 consume_string("null"); 233 return JsonValue(JsonValue::Type::Null); 234} 235 236JsonValue JsonParser::parse_undefined() 237{ 238 consume_string("undefined"); 239 return JsonValue(JsonValue::Type::Undefined); 240} 241 242JsonValue JsonParser::parse() 243{ 244 consume_whitespace(); 245 auto type_hint = peek(); 246 switch (type_hint) { 247 case '{': 248 return parse_object(); 249 case '[': 250 return parse_array(); 251 case '"': 252 return parse_string(); 253 case '-': 254 case '0': 255 case '1': 256 case '2': 257 case '3': 258 case '4': 259 case '5': 260 case '6': 261 case '7': 262 case '8': 263 case '9': 264 return parse_number(); 265 case 'f': 266 return parse_false(); 267 case 't': 268 return parse_true(); 269 case 'n': 270 return parse_null(); 271 case 'u': 272 return parse_undefined(); 273 } 274 275 return JsonValue(); 276} 277}