diff --git a/json/json.h b/json/json.h index 7cfa0573..7006a937 100644 --- a/json/json.h +++ b/json/json.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -105,6 +106,7 @@ class Parser { return v; } + // NOLINTNEXTLINE(misc-no-recursion) constexpr std::optional parse_value() { skip_whitespace(); auto c = peek(); @@ -121,11 +123,50 @@ class Parser { return parse_false(); case 'n': return parse_null(); + case '[': + return parse_array(); default: return std::nullopt; } } + // NOLINTNEXTLINE(misc-no-recursion) + constexpr std::optional parse_array() { + std::ignore = consume(); // '[' + if (peek() == ']') { + std::ignore = consume(); + return Array{}; + } + + Array array; + while (true) { + auto v = parse_value(); + if (!v) { + return std::nullopt; + } + + array.values.push_back(*std::move(v)); + skip_whitespace(); + + auto c = peek(); + if (!c) { + return std::nullopt; + } + + if (*c == ',') { + std::ignore = consume(); + continue; + } + + if (*c == ']') { + std::ignore = consume(); + return array; + } + + return std::nullopt; + } + } + constexpr std::optional parse_true() { std::ignore = consume(); // 't' auto r = consume(); diff --git a/json/json_test.cpp b/json/json_test.cpp index af3ee3bc..894144eb 100644 --- a/json/json_test.cpp +++ b/json/json_test.cpp @@ -9,6 +9,7 @@ #include int main() { + using json::Value; etest::Suite s{}; s.add_test("bad input", [](etest::IActions &a) { @@ -70,5 +71,19 @@ int main() { a.expect_eq(json::parse("null!"), std::nullopt); }); + s.add_test("array", [](etest::IActions &a) { + a.expect_eq(json::parse("[]"), Value{json::Array{}}); + a.expect_eq(json::parse(R"(["1"])"), Value{json::Array{{Value{"1"}}}}); + a.expect_eq(json::parse(R"([null, true, "hello", false, []])"), + Value{json::Array{ + {Value{json::Null{}}, Value{true}, Value{"hello"}, Value{false}, Value{json::Array{}}}, + }}); + + 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("[null,"), std::nullopt); + }); + return s.run(); }