Skip to content

Commit

Permalink
json: Support parsing objects
Browse files Browse the repository at this point in the history
  • Loading branch information
robinlinden committed Jan 14, 2025
1 parent 38639e2 commit 3757153
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 1 deletion.
56 changes: 55 additions & 1 deletion json/json.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,62 @@ class Parser {
return parse_null();
case '[':
return parse_array();
case '{':
return parse_object();
default:
return std::nullopt;
}
}

// NOLINTNEXTLINE(misc-no-recursion)
constexpr std::optional<Value> parse_object() {
std::ignore = consume(); // '{'
if (peek() == '}') {
std::ignore = consume();
return Object{};
}

Object object;
while (true) {
skip_whitespace();

auto key = parse_string();
if (!key) {
return std::nullopt;
}

skip_whitespace();
if (consume() != ':') {
return std::nullopt;
}

auto value = parse_value();
if (!value) {
return std::nullopt;
}

object.values[std::get<std::string>(*std::move(key))] = *std::move(value);
skip_whitespace();

auto c = peek();
if (!c) {
return std::nullopt;
}

if (*c == ',') {
std::ignore = consume();
continue;
}

if (*c == '}') {
std::ignore = consume();
return object;
}

return std::nullopt;
}
}

// NOLINTNEXTLINE(misc-no-recursion)
constexpr std::optional<Value> parse_array() {
std::ignore = consume(); // '['
Expand Down Expand Up @@ -206,7 +257,10 @@ class Parser {

constexpr std::optional<Value> parse_string() {
std::string value;
std::ignore = consume(); // '"'
if (consume() != '"') {
return std::nullopt;
}

while (auto c = consume()) {
if (*c == '"') {
return value;
Expand Down
22 changes: 22 additions & 0 deletions json/json_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,27 @@ int main() {
a.expect_eq(json::parse("[null,"), std::nullopt);
});

s.add_test("object", [](etest::IActions &a) {
a.expect_eq(json::parse("{}"), Value{json::Object{}});
a.expect_eq(json::parse(R"({"key": "value"})"), Value{json::Object{{{"key", Value{"value"}}}}});
a.expect_eq(json::parse(R"({"key": "value", "key2": "value2"})"),
Value{json::Object{{{"key", Value{"value"}}, {"key2", Value{"value2"}}}}});
a.expect_eq(json::parse(R"({"key": true, "key2": "value2", "key3": false})"),
Value{json::Object{{{"key", Value{true}}, {"key2", Value{"value2"}}, {"key3", Value{false}}}}});

a.expect_eq(json::parse(R"({"key": {"key": "value"}})"),
Value{json::Object{{{"key", Value{json::Object{{{"key", Value{"value"}}}}}}}}});

a.expect_eq(json::parse("{"), std::nullopt);
a.expect_eq(json::parse("{blah"), std::nullopt);
a.expect_eq(json::parse("{null"), std::nullopt);
a.expect_eq(json::parse(R"({"key")"), std::nullopt);
a.expect_eq(json::parse(R"({"key":)"), std::nullopt);
a.expect_eq(json::parse(R"({"key":asdf)"), std::nullopt);
a.expect_eq(json::parse(R"({"key":true)"), std::nullopt);
a.expect_eq(json::parse(R"({"key":true,)"), std::nullopt);
a.expect_eq(json::parse(R"({"key":true})"), Value{json::Object{{{"key", Value{true}}}}});
});

return s.run();
}

0 comments on commit 3757153

Please sign in to comment.