diff --git a/AK/JsonValue.cpp b/AK/JsonValue.cpp index 55f58f7a32..62b43d28ce 100644 --- a/AK/JsonValue.cpp +++ b/AK/JsonValue.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -175,4 +176,170 @@ String JsonValue::serialized() const return builder.to_string(); } +static bool is_whitespace(char ch) +{ + return ch == ' ' || ch == '\n' || ch == '\t' || ch == '\v' || ch == '\r'; +} + +JsonValue JsonValue::from_string(const StringView& input) +{ + int index = 0; + + auto peek = [&] { + return input[index]; + }; + + auto consume = [&]() -> char { + if (index < input.length()) + return input[index++]; + return '\0'; + }; + + auto consume_while = [&](auto condition) { + while (condition(peek())) + consume(); + }; + + auto extract_while = [&](auto condition) { + StringBuilder builder; + while (condition(peek())) + builder.append(consume()); + return builder.to_string(); + }; + + auto consume_whitespace = [&] { + consume_while([](char ch) { return is_whitespace(ch); }); + }; + + auto consume_specific = [&](char expected_ch) { + char consumed_ch = consume(); + ASSERT(consumed_ch == expected_ch); + }; + + Function parse; + + auto parse_object_member = [&](JsonObject& object) { + consume_whitespace(); + consume_specific('"'); + auto name = extract_while([](char ch) { return ch != '"'; }); + consume_specific('"'); + consume_whitespace(); + consume_specific(':'); + consume_whitespace(); + auto value = parse(); + object.set(name, value); + }; + + auto parse_object = [&]() -> JsonValue { + JsonObject object; + consume_specific('{'); + for (;;) { + consume_whitespace(); + if (peek() == '}') + break; + parse_object_member(object); + consume_whitespace(); + if (peek() == '}') + break; + consume_specific(','); + } + consume_specific('}'); + return object; + }; + + auto parse_array = [&]() -> JsonValue { + JsonArray array; + consume_specific('['); + for (;;) { + consume_whitespace(); + if (peek() == ']') + break; + array.append(parse()); + consume_whitespace(); + if (peek() == ']') + break; + consume_specific(','); + } + consume_whitespace(); + consume_specific(']'); + return array; + }; + + auto parse_string = [&]() -> JsonValue { + consume_specific('"'); + auto string = extract_while([](char ch) { return ch != '"'; }); + consume_specific('"'); + return JsonValue(string); + }; + + auto parse_number = [&]() -> JsonValue { + auto number_string = extract_while([](char ch) { return ch == '-' || (ch >= '0' && ch <= '9'); }); + bool ok; + auto value = JsonValue(number_string.to_int(ok)); + ASSERT(ok); + return value; + }; + + auto consume_string = [&](const char* str) { + for (size_t i = 0, length = strlen(str); i < length; ++i) + consume_specific(str[i]); + }; + + auto parse_true = [&]() -> JsonValue { + consume_string("true"); + return JsonValue(true); + }; + + auto parse_false = [&]() -> JsonValue { + consume_string("false"); + return JsonValue(false); + }; + + auto parse_null = [&]() -> JsonValue { + consume_string("null"); + return JsonValue(JsonValue::Type::Null); + }; + + auto parse_undefined = [&]() -> JsonValue { + consume_string("undefined"); + return JsonValue(JsonValue::Type::Undefined); + }; + + parse = [&]() -> JsonValue { + consume_whitespace(); + auto type_hint = peek(); + switch (type_hint) { + case '{': + return parse_object(); + case '[': + return parse_array(); + case '"': + return parse_string(); + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return parse_number(); + case 'f': + return parse_false(); + case 't': + return parse_true(); + case 'n': + return parse_null(); + case 'u': + return parse_undefined(); + } + ASSERT_NOT_REACHED(); + }; + + return parse(); +} + } diff --git a/AK/JsonValue.h b/AK/JsonValue.h index 211086d2d1..439429d9d7 100644 --- a/AK/JsonValue.h +++ b/AK/JsonValue.h @@ -21,6 +21,8 @@ public: Object, }; + static JsonValue from_string(const StringView&); + explicit JsonValue(Type = Type::Null); ~JsonValue() { clear(); }