Index: clangd/JSONExpr.h =================================================================== --- clangd/JSONExpr.h +++ clangd/JSONExpr.h @@ -172,7 +172,7 @@ return llvm::None; } llvm::Optional boolean() const { - if (LLVM_LIKELY(Type == T_Null)) + if (LLVM_LIKELY(Type == T_Boolean)) return as(); return llvm::None; } @@ -292,6 +292,63 @@ Expr &operator[](ObjectKey &&K) { return emplace(std::move(K), Expr(nullptr)).first->second; } + + // Look up a property, returning nullptr if it doesn't exist. + json::Expr *get(const ObjectKey &K) { + auto I = find(K); + if (I == end()) + return nullptr; + return &I->second; + } + const json::Expr *get(const ObjectKey &K) const { + auto I = find(K); + if (I == end()) + return nullptr; + return &I->second; + } + // Typed accessors return None/nullptr if + // - the property doesn't exist + // - or it has the wrong type + llvm::Optional null(const ObjectKey &K) const { + if (auto *V = get(K)) + return V->null(); + return llvm::None; + } + llvm::Optional boolean(const ObjectKey &K) const { + if (auto *V = get(K)) + return V->boolean(); + return llvm::None; + } + llvm::Optional number(const ObjectKey &K) const { + if (auto *V = get(K)) + return V->number(); + return llvm::None; + } + llvm::Optional string(const ObjectKey &K) const { + if (auto *V = get(K)) + return V->string(); + return llvm::None; + } + const ObjectExpr *object(const ObjectKey &K) const { + if (auto *V = get(K)) + return V->object(); + return nullptr; + } + ObjectExpr *object(const ObjectKey &K) { + if (auto *V = get(K)) + return V->object(); + return nullptr; + } + const ArrayExpr *array(const ObjectKey &K) const { + if (auto *V = get(K)) + return V->array(); + return nullptr; + } + ArrayExpr *array(const ObjectKey &K) { + if (auto *V = get(K)) + return V->array(); + return nullptr; + } }; class ArrayExpr : public std::vector { @@ -306,6 +363,24 @@ for (const auto &V : C) emplace_back(V); } + + // Typed accessors return None/nullptr if the element has the wrong type. + llvm::Optional null(size_t I) const { + return (*this)[I].null(); + } + llvm::Optional boolean(size_t I) const { + return (*this)[I].boolean(); + } + llvm::Optional number(size_t I) const { + return (*this)[I].number(); + } + llvm::Optional string(size_t I) const { + return (*this)[I].string(); + } + const ObjectExpr *object(size_t I) const { return (*this)[I].object(); } + ObjectExpr *object(size_t I) { return (*this)[I].object(); } + const ArrayExpr *array(size_t I) const { return (*this)[I].array(); } + ArrayExpr *array(size_t I) { return (*this)[I].array(); } }; private: Index: unittests/clangd/JSONExprTests.cpp =================================================================== --- unittests/clangd/JSONExprTests.cpp +++ unittests/clangd/JSONExprTests.cpp @@ -167,7 +167,6 @@ ExpectErr("Unexpected EOF", ""); ExpectErr("Unexpected EOF", "["); ExpectErr("Text after end of document", "[][]"); - ExpectErr("Text after end of document", "[][]"); ExpectErr("Invalid bareword", "fuzzy"); ExpectErr("Expected , or ]", "[2?]"); ExpectErr("Expected object key", "{a:2}"); @@ -185,6 +184,49 @@ })"); } +TEST(JSONTest, Inspection) { + llvm::Expected Doc = parse(R"( + { + "null": null, + "boolean": false, + "number": 2.78, + "string": "json", + "array": [null, true, 3.14, "hello", [1,2,3], {"time": "arrow"}], + "object": {"fruit": "banana"} + } + )"); + EXPECT_TRUE(!!Doc); + + obj *O = Doc->object(); + ASSERT_TRUE(O); + + EXPECT_FALSE(O->null("missing")); + EXPECT_FALSE(O->null("boolean")); + EXPECT_TRUE(O->null("null")); + + EXPECT_EQ(O->number("number"), llvm::Optional(2.78)); + EXPECT_EQ(O->string("string"), llvm::Optional("json")); + ASSERT_FALSE(O->object("missing")); + ASSERT_FALSE(O->object("array")); + ASSERT_TRUE(O->object("object")); + EXPECT_EQ(*O->object("object"), (obj{{"fruit", "banana"}})); + + ary *A = O->array("array"); + ASSERT_TRUE(A); + EXPECT_EQ(A->boolean(1), llvm::Optional(true)); + ASSERT_TRUE(A->array(4)); + EXPECT_EQ(*A->array(4), (ary{1, 2, 3})); + int I = 0; + for (Expr &E : *A) { + if (I++ == 5) { + ASSERT_TRUE(E.object()); + EXPECT_EQ(E.object()->string("time"), + llvm::Optional("arrow")); + } else + EXPECT_FALSE(E.object()); + } +} + } // namespace } // namespace json } // namespace clangd