forked from OSchip/llvm-project
[clangd] Add missing (but documented!) JSONExpr typed accessors
Summary: Noticed this when I tried to port the Protocol.h parsers. And tests for the inspect API, which caught a small bug. Reviewers: ioeric Subscribers: ilya-biryukov Differential Revision: https://reviews.llvm.org/D40399 llvm-svn: 319157
This commit is contained in:
parent
b4f28deda0
commit
1bb272bc74
|
@ -161,7 +161,7 @@ bool Parser::parseExpr(Expr &Out) {
|
|||
}
|
||||
case '[': {
|
||||
Out = json::ary{};
|
||||
json::ary &A = *Out.array();
|
||||
json::ary &A = *Out.asArray();
|
||||
eatWhitespace();
|
||||
if (peek() == ']') {
|
||||
++P;
|
||||
|
@ -185,7 +185,7 @@ bool Parser::parseExpr(Expr &Out) {
|
|||
}
|
||||
case '{': {
|
||||
Out = json::obj{};
|
||||
json::obj &O = *Out.object();
|
||||
json::obj &O = *Out.asObject();
|
||||
eatWhitespace();
|
||||
if (peek() == '}') {
|
||||
++P;
|
||||
|
@ -507,17 +507,17 @@ bool operator==(const Expr &L, const Expr &R) {
|
|||
return false;
|
||||
switch (L.kind()) {
|
||||
case Expr::Null:
|
||||
return L.null() == R.null();
|
||||
return *L.asNull() == *R.asNull();
|
||||
case Expr::Boolean:
|
||||
return L.boolean() == R.boolean();
|
||||
return *L.asBoolean() == *R.asBoolean();
|
||||
case Expr::Number:
|
||||
return L.boolean() == R.boolean();
|
||||
return *L.asNumber() == *R.asNumber();
|
||||
case Expr::String:
|
||||
return L.string() == R.string();
|
||||
return *L.asString() == *R.asString();
|
||||
case Expr::Array:
|
||||
return *L.array() == *R.array();
|
||||
return *L.asArray() == *R.asArray();
|
||||
case Expr::Object:
|
||||
return *L.object() == *R.object();
|
||||
return *L.asObject() == *R.asObject();
|
||||
}
|
||||
llvm_unreachable("Unknown expression kind");
|
||||
}
|
||||
|
|
|
@ -55,14 +55,14 @@ namespace json {
|
|||
// object (json::obj)
|
||||
//
|
||||
// The kind can be queried directly, or implicitly via the typed accessors:
|
||||
// if (Optional<StringRef> S = E.string())
|
||||
// if (Optional<StringRef> S = E.asString()
|
||||
// assert(E.kind() == Expr::String);
|
||||
//
|
||||
// Array and Object also have typed indexing accessors for easy traversal:
|
||||
// Expected<Expr> E = parse(R"( {"options": {"font": "sans-serif"}} )");
|
||||
// if (json::obj* O = E->object())
|
||||
// if (json::obj* Opts = O->object("options"))
|
||||
// if (Optional<StringRef> Font = Opts->string("font"))
|
||||
// if (json::obj* O = E->asObject())
|
||||
// if (json::obj* Opts = O->getObject("options"))
|
||||
// if (Optional<StringRef> Font = Opts->getString("font"))
|
||||
// assert(Opts->at("font").kind() == Expr::String);
|
||||
//
|
||||
// === Serialization ===
|
||||
|
@ -166,38 +166,38 @@ public:
|
|||
}
|
||||
|
||||
// Typed accessors return None/nullptr if the Expr is not of this type.
|
||||
llvm::Optional<std::nullptr_t> null() const {
|
||||
llvm::Optional<std::nullptr_t> asNull() const {
|
||||
if (LLVM_LIKELY(Type == T_Null))
|
||||
return nullptr;
|
||||
return llvm::None;
|
||||
}
|
||||
llvm::Optional<bool> boolean() const {
|
||||
if (LLVM_LIKELY(Type == T_Null))
|
||||
llvm::Optional<bool> asBoolean() const {
|
||||
if (LLVM_LIKELY(Type == T_Boolean))
|
||||
return as<bool>();
|
||||
return llvm::None;
|
||||
}
|
||||
llvm::Optional<double> number() const {
|
||||
llvm::Optional<double> asNumber() const {
|
||||
if (LLVM_LIKELY(Type == T_Number))
|
||||
return as<double>();
|
||||
return llvm::None;
|
||||
}
|
||||
llvm::Optional<llvm::StringRef> string() const {
|
||||
llvm::Optional<llvm::StringRef> asString() const {
|
||||
if (Type == T_String)
|
||||
return llvm::StringRef(as<std::string>());
|
||||
if (LLVM_LIKELY(Type == T_StringRef))
|
||||
return as<llvm::StringRef>();
|
||||
return llvm::None;
|
||||
}
|
||||
const ObjectExpr *object() const {
|
||||
const ObjectExpr *asObject() const {
|
||||
return LLVM_LIKELY(Type == T_Object) ? &as<ObjectExpr>() : nullptr;
|
||||
}
|
||||
ObjectExpr *object() {
|
||||
ObjectExpr *asObject() {
|
||||
return LLVM_LIKELY(Type == T_Object) ? &as<ObjectExpr>() : nullptr;
|
||||
}
|
||||
const ArrayExpr *array() const {
|
||||
const ArrayExpr *asArray() const {
|
||||
return LLVM_LIKELY(Type == T_Array) ? &as<ArrayExpr>() : nullptr;
|
||||
}
|
||||
ArrayExpr *array() {
|
||||
ArrayExpr *asArray() {
|
||||
return LLVM_LIKELY(Type == T_Array) ? &as<ArrayExpr>() : nullptr;
|
||||
}
|
||||
|
||||
|
@ -292,6 +292,63 @@ public:
|
|||
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<std::nullptr_t> getNull(const ObjectKey &K) const {
|
||||
if (auto *V = get(K))
|
||||
return V->asNull();
|
||||
return llvm::None;
|
||||
}
|
||||
llvm::Optional<bool> getBoolean(const ObjectKey &K) const {
|
||||
if (auto *V = get(K))
|
||||
return V->asBoolean();
|
||||
return llvm::None;
|
||||
}
|
||||
llvm::Optional<double> getNumber(const ObjectKey &K) const {
|
||||
if (auto *V = get(K))
|
||||
return V->asNumber();
|
||||
return llvm::None;
|
||||
}
|
||||
llvm::Optional<llvm::StringRef> getString(const ObjectKey &K) const {
|
||||
if (auto *V = get(K))
|
||||
return V->asString();
|
||||
return llvm::None;
|
||||
}
|
||||
const ObjectExpr *getObject(const ObjectKey &K) const {
|
||||
if (auto *V = get(K))
|
||||
return V->asObject();
|
||||
return nullptr;
|
||||
}
|
||||
ObjectExpr *getObject(const ObjectKey &K) {
|
||||
if (auto *V = get(K))
|
||||
return V->asObject();
|
||||
return nullptr;
|
||||
}
|
||||
const ArrayExpr *getArray(const ObjectKey &K) const {
|
||||
if (auto *V = get(K))
|
||||
return V->asArray();
|
||||
return nullptr;
|
||||
}
|
||||
ArrayExpr *getArray(const ObjectKey &K) {
|
||||
if (auto *V = get(K))
|
||||
return V->asArray();
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class ArrayExpr : public std::vector<Expr> {
|
||||
|
@ -306,6 +363,26 @@ public:
|
|||
for (const auto &V : C)
|
||||
emplace_back(V);
|
||||
}
|
||||
|
||||
// Typed accessors return None/nullptr if the element has the wrong type.
|
||||
llvm::Optional<std::nullptr_t> getNull(size_t I) const {
|
||||
return (*this)[I].asNull();
|
||||
}
|
||||
llvm::Optional<bool> getBoolean(size_t I) const {
|
||||
return (*this)[I].asBoolean();
|
||||
}
|
||||
llvm::Optional<double> getNumber(size_t I) const {
|
||||
return (*this)[I].asNumber();
|
||||
}
|
||||
llvm::Optional<llvm::StringRef> getString(size_t I) const {
|
||||
return (*this)[I].asString();
|
||||
}
|
||||
const ObjectExpr *getObject(size_t I) const {
|
||||
return (*this)[I].asObject();
|
||||
}
|
||||
ObjectExpr *getObject(size_t I) { return (*this)[I].asObject(); }
|
||||
const ArrayExpr *getArray(size_t I) const { return (*this)[I].asArray(); }
|
||||
ArrayExpr *getArray(size_t I) { return (*this)[I].asArray(); }
|
||||
};
|
||||
|
||||
private:
|
||||
|
|
|
@ -167,7 +167,6 @@ TEST(JSONTest, ParseErrors) {
|
|||
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, ParseErrors) {
|
|||
})");
|
||||
}
|
||||
|
||||
TEST(JSONTest, Inspection) {
|
||||
llvm::Expected<Expr> 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->asObject();
|
||||
ASSERT_TRUE(O);
|
||||
|
||||
EXPECT_FALSE(O->getNull("missing"));
|
||||
EXPECT_FALSE(O->getNull("boolean"));
|
||||
EXPECT_TRUE(O->getNull("null"));
|
||||
|
||||
EXPECT_EQ(O->getNumber("number"), llvm::Optional<double>(2.78));
|
||||
EXPECT_EQ(O->getString("string"), llvm::Optional<llvm::StringRef>("json"));
|
||||
ASSERT_FALSE(O->getObject("missing"));
|
||||
ASSERT_FALSE(O->getObject("array"));
|
||||
ASSERT_TRUE(O->getObject("object"));
|
||||
EXPECT_EQ(*O->getObject("object"), (obj{{"fruit", "banana"}}));
|
||||
|
||||
ary *A = O->getArray("array");
|
||||
ASSERT_TRUE(A);
|
||||
EXPECT_EQ(A->getBoolean(1), llvm::Optional<bool>(true));
|
||||
ASSERT_TRUE(A->getArray(4));
|
||||
EXPECT_EQ(*A->getArray(4), (ary{1, 2, 3}));
|
||||
int I = 0;
|
||||
for (Expr &E : *A) {
|
||||
if (I++ == 5) {
|
||||
ASSERT_TRUE(E.asObject());
|
||||
EXPECT_EQ(E.asObject()->getString("time"),
|
||||
llvm::Optional<llvm::StringRef>("arrow"));
|
||||
} else
|
||||
EXPECT_FALSE(E.asObject());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace json
|
||||
} // namespace clangd
|
||||
|
|
Loading…
Reference in New Issue