Serenity Operating System
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#include <AK/Memory.h>
31
32namespace AK {
33
34static inline bool is_whitespace(char ch)
35{
36 return ch == ' ' || ch == '\n' || ch == '\t' || ch == '\v' || ch == '\r';
37}
38
39char JsonParser::peek() const
40{
41 if (m_index < m_input.length())
42 return m_input[m_index];
43 return '\0';
44}
45
46char JsonParser::consume()
47{
48 if (m_index < m_input.length())
49 return m_input[m_index++];
50 return '\0';
51}
52
53template<typename C>
54void JsonParser::consume_while(C condition)
55{
56 while (condition(peek()))
57 consume();
58}
59
60void JsonParser::consume_whitespace()
61{
62 consume_while([](char ch) { return is_whitespace(ch); });
63}
64
65void JsonParser::consume_specific(char expected_ch)
66{
67 char consumed_ch = consume();
68 ASSERT(consumed_ch == expected_ch);
69}
70
71String JsonParser::consume_quoted_string()
72{
73 consume_specific('"');
74 Vector<char, 1024> buffer;
75
76 for (;;) {
77 size_t peek_index = m_index;
78 char ch = 0;
79 for (;;) {
80 if (peek_index == m_input.length())
81 break;
82 ch = m_input[peek_index];
83 if (ch == '"' || ch == '\\')
84 break;
85 ++peek_index;
86 }
87
88 if (peek_index != m_index) {
89 buffer.append(m_input.characters_without_null_termination() + m_index, peek_index - m_index);
90 m_index = peek_index;
91 }
92
93 if (m_index == m_input.length())
94 break;
95 if (ch == '"')
96 break;
97 if (ch != '\\') {
98 buffer.append(consume());
99 continue;
100 }
101 consume();
102 char escaped_ch = consume();
103 switch (escaped_ch) {
104 case 'n':
105 buffer.append('\n');
106 break;
107 case 'r':
108 buffer.append('\r');
109 break;
110 case 't':
111 buffer.append('\t');
112 break;
113 case 'b':
114 buffer.append('\b');
115 break;
116 case 'f':
117 buffer.append('\f');
118 break;
119 case 'u':
120 consume();
121 consume();
122 consume();
123 consume();
124 // FIXME: This is obviously not correct, but we don't have non-ASCII support so meh.
125 buffer.append('?');
126 break;
127 default:
128 buffer.append(escaped_ch);
129 break;
130 }
131 }
132 consume_specific('"');
133
134 if (buffer.is_empty())
135 return String::empty();
136
137 auto& last_string_starting_with_character = m_last_string_starting_with_character[(u8)buffer.first()];
138 if (last_string_starting_with_character.length() == buffer.size()) {
139 if (!memcmp(last_string_starting_with_character.characters(), buffer.data(), buffer.size()))
140 return last_string_starting_with_character;
141 }
142
143 last_string_starting_with_character = String::copy(buffer);
144 return last_string_starting_with_character;
145}
146
147JsonObject JsonParser::parse_object()
148{
149 JsonObject object;
150 consume_specific('{');
151 for (;;) {
152 consume_whitespace();
153 if (peek() == '}')
154 break;
155 consume_whitespace();
156 auto name = consume_quoted_string();
157 consume_whitespace();
158 consume_specific(':');
159 consume_whitespace();
160 auto value = parse();
161 object.set(name, move(value));
162 consume_whitespace();
163 if (peek() == '}')
164 break;
165 consume_specific(',');
166 }
167 consume_specific('}');
168 return object;
169}
170
171JsonArray JsonParser::parse_array()
172{
173 JsonArray array;
174 consume_specific('[');
175 for (;;) {
176 consume_whitespace();
177 if (peek() == ']')
178 break;
179 array.append(parse());
180 consume_whitespace();
181 if (peek() == ']')
182 break;
183 consume_specific(',');
184 }
185 consume_whitespace();
186 consume_specific(']');
187 return array;
188}
189
190JsonValue JsonParser::parse_string()
191{
192 return consume_quoted_string();
193}
194
195JsonValue JsonParser::parse_number()
196{
197 bool ok;
198 JsonValue value;
199 Vector<char, 128> number_buffer;
200 Vector<char, 128> fraction_buffer;
201
202 bool is_double = false;
203 for (;;) {
204 char ch = peek();
205 if (ch == '.') {
206 is_double = true;
207 ++m_index;
208 continue;
209 }
210 if (ch == '-' || (ch >= '0' && ch <= '9')) {
211 if (is_double)
212 fraction_buffer.append(ch);
213 else
214 number_buffer.append(ch);
215 ++m_index;
216 continue;
217 }
218 break;
219 }
220
221 StringView number_string(number_buffer.data(), number_buffer.size());
222 StringView fraction_string(fraction_buffer.data(), fraction_buffer.size());
223
224#ifndef KERNEL
225 if (is_double) {
226 int whole = number_string.to_uint(ok);
227 if (!ok)
228 whole = number_string.to_int(ok);
229 ASSERT(ok);
230
231 int fraction = fraction_string.to_uint(ok);
232 fraction *= (whole < 0) ? -1 : 1;
233 ASSERT(ok);
234
235 auto divider = 1;
236 for (size_t i = 0; i < fraction_buffer.size(); ++i) {
237 divider *= 10;
238 }
239 value = JsonValue((double)whole + ((double)fraction / divider));
240 } else {
241#endif
242 value = JsonValue(number_string.to_uint(ok));
243 if (!ok)
244 value = JsonValue(number_string.to_int(ok));
245 ASSERT(ok);
246#ifndef KERNEL
247 }
248#endif
249
250 return value;
251}
252
253void JsonParser::consume_string(const char* str)
254{
255 for (size_t i = 0, length = strlen(str); i < length; ++i)
256 consume_specific(str[i]);
257}
258
259JsonValue JsonParser::parse_true()
260{
261 consume_string("true");
262 return JsonValue(true);
263}
264
265JsonValue JsonParser::parse_false()
266{
267 consume_string("false");
268 return JsonValue(false);
269}
270
271JsonValue JsonParser::parse_null()
272{
273 consume_string("null");
274 return JsonValue(JsonValue::Type::Null);
275}
276
277JsonValue JsonParser::parse_undefined()
278{
279 consume_string("undefined");
280 return JsonValue(JsonValue::Type::Undefined);
281}
282
283JsonValue JsonParser::parse()
284{
285 consume_whitespace();
286 auto type_hint = peek();
287 switch (type_hint) {
288 case '{':
289 return parse_object();
290 case '[':
291 return parse_array();
292 case '"':
293 return parse_string();
294 case '-':
295 case '0':
296 case '1':
297 case '2':
298 case '3':
299 case '4':
300 case '5':
301 case '6':
302 case '7':
303 case '8':
304 case '9':
305 return parse_number();
306 case 'f':
307 return parse_false();
308 case 't':
309 return parse_true();
310 case 'n':
311 return parse_null();
312 case 'u':
313 return parse_undefined();
314 }
315
316 return JsonValue();
317}
318}