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
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}