diff --git a/AK/JsonParser.cpp b/AK/JsonParser.cpp new file mode 100644 index 00000000000..c662d087abe --- /dev/null +++ b/AK/JsonParser.cpp @@ -0,0 +1,185 @@ +#include +#include +#include +#include + +namespace AK { + +static inline bool is_whitespace(char ch) +{ + return ch == ' ' || ch == '\n' || ch == '\t' || ch == '\v' || ch == '\r'; +} + +char JsonParser::peek() const +{ + if (m_index < m_input.length()) + return m_input[m_index]; + return '\0'; +} + +char JsonParser::consume() +{ + if (m_index < m_input.length()) + return m_input[m_index++]; + return '\0'; +} + +template +void JsonParser::consume_while(C condition) +{ + while (condition(peek())) + consume(); +} + +template +String JsonParser::extract_while(C condition) +{ + StringBuilder builder; + while (condition(peek())) + builder.append(consume()); + return builder.to_string(); +}; + +void JsonParser::consume_whitespace() +{ + consume_while([](char ch) { return is_whitespace(ch); }); +} + +void JsonParser::consume_specific(char expected_ch) +{ + char consumed_ch = consume(); + ASSERT(consumed_ch == expected_ch); +} + +String JsonParser::consume_quoted_string() +{ + consume_specific('"'); + auto string = extract_while([](char ch) { return ch != '"'; }); + consume_specific('"'); + return string; +} + +JsonValue JsonParser::parse_object() +{ + JsonObject object; + consume_specific('{'); + for (;;) { + consume_whitespace(); + if (peek() == '}') + break; + consume_whitespace(); + auto name = consume_quoted_string(); + consume_whitespace(); + consume_specific(':'); + consume_whitespace(); + auto value = parse(); + object.set(name, value); + consume_whitespace(); + if (peek() == '}') + break; + consume_specific(','); + } + consume_specific('}'); + return object; +} + +JsonValue JsonParser::parse_array() +{ + 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; +} + +JsonValue JsonParser::parse_string() +{ + return consume_quoted_string(); +} + +JsonValue JsonParser::parse_number() +{ + 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; +} + +void JsonParser::consume_string(const char* str) +{ + for (size_t i = 0, length = strlen(str); i < length; ++i) + consume_specific(str[i]); +} + +JsonValue JsonParser::parse_true() +{ + consume_string("true"); + return JsonValue(true); +} + +JsonValue JsonParser::parse_false() +{ + consume_string("false"); + return JsonValue(false); +} + +JsonValue JsonParser::parse_null() +{ + consume_string("null"); + return JsonValue(JsonValue::Type::Null); +} + +JsonValue JsonParser::parse_undefined() +{ + consume_string("undefined"); + return JsonValue(JsonValue::Type::Undefined); +} + +JsonValue JsonParser::parse() +{ + 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(); + } + + return JsonValue(); +} + +} diff --git a/AK/JsonParser.h b/AK/JsonParser.h new file mode 100644 index 00000000000..76f7d7e5bfa --- /dev/null +++ b/AK/JsonParser.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +namespace AK { + +class JsonParser { +public: + explicit JsonParser(const StringView& input) + : m_input(input) + { + } + ~JsonParser() + { + } + + JsonValue parse(); + +private: + char peek() const; + char consume(); + void consume_whitespace(); + void consume_specific(char expected_ch); + void consume_string(const char*); + String consume_quoted_string(); + JsonValue parse_array(); + JsonValue parse_object(); + JsonValue parse_number(); + JsonValue parse_string(); + JsonValue parse_false(); + JsonValue parse_true(); + JsonValue parse_null(); + JsonValue parse_undefined(); + + template + void consume_while(C); + + template + String extract_while(C); + + StringView m_input; + int m_index { 0 }; +}; + +} + +using AK::JsonParser; diff --git a/AK/JsonValue.cpp b/AK/JsonValue.cpp index 62b43d28ce8..48ca1effd53 100644 --- a/AK/JsonValue.cpp +++ b/AK/JsonValue.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -176,170 +177,9 @@ 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(); + return JsonParser(input).parse(); } } diff --git a/LibC/Makefile b/LibC/Makefile index c2822e2df84..8892be807fa 100644 --- a/LibC/Makefile +++ b/LibC/Makefile @@ -10,6 +10,7 @@ AK_OBJS = \ ../AK/JsonValue.o \ ../AK/JsonArray.o \ ../AK/JsonObject.o \ + ../AK/JsonParser.o \ ../AK/MappedFile.o LIBC_OBJS = \