summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clangd/JSONExpr.h
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clangd/JSONExpr.h')
-rw-r--r--clang-tools-extra/clangd/JSONExpr.h169
1 files changed, 145 insertions, 24 deletions
diff --git a/clang-tools-extra/clangd/JSONExpr.h b/clang-tools-extra/clangd/JSONExpr.h
index b9cf808b79e..f9114ce1a65 100644
--- a/clang-tools-extra/clangd/JSONExpr.h
+++ b/clang-tools-extra/clangd/JSONExpr.h
@@ -1,4 +1,4 @@
-//===--- JSONExpr.h - composable JSON expressions ---------------*- C++ -*-===//
+//===--- JSONExpr.h - JSON expressions, parsing and serialization - C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -7,6 +7,8 @@
//
//===---------------------------------------------------------------------===//
+// FIXME: rename to JSON.h now that the scope is wider?
+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSON_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSON_H
@@ -14,6 +16,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/raw_ostream.h"
@@ -21,10 +24,12 @@ namespace clang {
namespace clangd {
namespace json {
-// An Expr is an opaque temporary JSON structure used to compose documents.
+// An Expr is an JSON value of unknown type.
// They can be copied, but should generally be moved.
//
-// You can implicitly construct literals from:
+// === Composing expressions ===
+//
+// You can implicitly construct Exprs from:
// - strings: std::string, SmallString, formatv, StringRef, char*
// (char*, and StringRef are references, not copies!)
// - numbers
@@ -39,25 +44,62 @@ namespace json {
// These can be list-initialized, or used to build up collections in a loop.
// json::ary(Collection) converts all items in a collection to Exprs.
//
+// === Inspecting expressions ===
+//
+// Each Expr is one of the JSON kinds:
+// null (nullptr_t)
+// boolean (bool)
+// number (double)
+// string (StringRef)
+// array (json::ary)
+// object (json::obj)
+//
+// The kind can be queried directly, or implicitly via the typed accessors:
+// if (Optional<StringRef> S = E.string())
+// 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"))
+// assert(Opts->at("font").kind() == Expr::String);
+//
+// === Serialization ===
+//
// Exprs can be serialized to JSON:
// 1) raw_ostream << Expr // Basic formatting.
// 2) raw_ostream << formatv("{0}", Expr) // Basic formatting.
// 3) raw_ostream << formatv("{0:2}", Expr) // Pretty-print with indent 2.
+//
+// And parsed:
+// Expected<Expr> E = json::parse("[1, 2, null]");
+// assert(E && E->kind() == Expr::Array);
class Expr {
public:
- class Object;
+ enum Kind {
+ Null,
+ Boolean,
+ Number,
+ String,
+ Array,
+ Object,
+ };
+ class ObjectExpr;
class ObjectKey;
- class Array;
+ class ArrayExpr;
// It would be nice to have Expr() be null. But that would make {} null too...
Expr(const Expr &M) { copyFrom(M); }
Expr(Expr &&M) { moveFrom(std::move(M)); }
// "cheating" move-constructor for moving from initializer_list.
Expr(const Expr &&M) { moveFrom(std::move(M)); }
- Expr(std::initializer_list<Expr> Elements) : Expr(Array(Elements)) {}
- Expr(Array &&Elements) : Type(T_Array) { create<Array>(std::move(Elements)); }
- Expr(Object &&Properties) : Type(T_Object) {
- create<Object>(std::move(Properties));
+ Expr(std::initializer_list<Expr> Elements) : Expr(ArrayExpr(Elements)) {}
+ Expr(ArrayExpr &&Elements) : Type(T_Array) {
+ create<ArrayExpr>(std::move(Elements));
+ }
+ Expr(ObjectExpr &&Properties) : Type(T_Object) {
+ create<ObjectExpr>(std::move(Properties));
}
// Strings: types with value semantics.
Expr(std::string &&V) : Type(T_String) { create<std::string>(std::move(V)); }
@@ -104,6 +146,60 @@ public:
}
~Expr() { destroy(); }
+ Kind kind() const {
+ switch (Type) {
+ case T_Null:
+ return Null;
+ case T_Boolean:
+ return Boolean;
+ case T_Number:
+ return Number;
+ case T_String:
+ case T_StringRef:
+ return String;
+ case T_Object:
+ return Object;
+ case T_Array:
+ return Array;
+ }
+ }
+
+ // Typed accessors return None/nullptr if the Expr is not of this type.
+ llvm::Optional<std::nullptr_t> null() const {
+ if (LLVM_LIKELY(Type == T_Null))
+ return nullptr;
+ return llvm::None;
+ }
+ llvm::Optional<bool> boolean() const {
+ if (LLVM_LIKELY(Type == T_Null))
+ return as<bool>();
+ return llvm::None;
+ }
+ llvm::Optional<double> number() const {
+ if (LLVM_LIKELY(Type == T_Number))
+ return as<double>();
+ return llvm::None;
+ }
+ llvm::Optional<llvm::StringRef> string() 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 {
+ return LLVM_LIKELY(Type == T_Object) ? &as<ObjectExpr>() : nullptr;
+ }
+ ObjectExpr *object() {
+ return LLVM_LIKELY(Type == T_Object) ? &as<ObjectExpr>() : nullptr;
+ }
+ const ArrayExpr *array() const {
+ return LLVM_LIKELY(Type == T_Array) ? &as<ArrayExpr>() : nullptr;
+ }
+ ArrayExpr *array() {
+ return LLVM_LIKELY(Type == T_Array) ? &as<ArrayExpr>() : nullptr;
+ }
+
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Expr &);
private:
@@ -137,10 +233,8 @@ private:
mutable ExprType Type;
public:
- // ObjectKey is a used to capture keys in Expr::Objects. It's like Expr but:
+ // ObjectKey is a used to capture keys in Expr::ObjectExpr. Like Expr but:
// - only strings are allowed
- // - it's copyable (for std::map)
- // - we're slightly more eager to copy, to allow efficient key compares
// - it's optimized for the string literal case (Owned == nullptr)
class ObjectKey {
public:
@@ -183,12 +277,12 @@ public:
llvm::StringRef Data;
};
- class Object : public std::map<ObjectKey, Expr> {
+ class ObjectExpr : public std::map<ObjectKey, Expr> {
public:
- explicit Object() {}
+ explicit ObjectExpr() {}
// Use a custom struct for list-init, because pair forces extra copies.
struct KV;
- explicit Object(std::initializer_list<KV> Properties);
+ explicit ObjectExpr(std::initializer_list<KV> Properties);
// Allow [] as if Expr was default-constructible as null.
Expr &operator[](const ObjectKey &K) {
@@ -199,15 +293,15 @@ public:
}
};
- class Array : public std::vector<Expr> {
+ class ArrayExpr : public std::vector<Expr> {
public:
- explicit Array() {}
- explicit Array(std::initializer_list<Expr> Elements) {
+ explicit ArrayExpr() {}
+ explicit ArrayExpr(std::initializer_list<Expr> Elements) {
reserve(Elements.size());
for (const Expr &V : Elements)
emplace_back(std::move(V));
};
- template <typename Collection> explicit Array(const Collection &C) {
+ template <typename Collection> explicit ArrayExpr(const Collection &C) {
for (const auto &V : C)
emplace_back(V);
}
@@ -215,23 +309,50 @@ public:
private:
mutable llvm::AlignedCharArrayUnion<bool, double, llvm::StringRef,
- std::string, Array, Object>
+ std::string, ArrayExpr, ObjectExpr>
Union;
};
-struct Expr::Object::KV {
+bool operator==(const Expr &, const Expr &);
+inline bool operator!=(const Expr &L, const Expr &R) { return !(L == R); }
+inline bool operator==(const Expr::ObjectKey &L, const Expr::ObjectKey &R) {
+ return llvm::StringRef(L) == llvm::StringRef(R);
+}
+inline bool operator!=(const Expr::ObjectKey &L, const Expr::ObjectKey &R) {
+ return !(L == R);
+}
+
+struct Expr::ObjectExpr::KV {
ObjectKey K;
Expr V;
};
-inline Expr::Object::Object(std::initializer_list<KV> Properties) {
+inline Expr::ObjectExpr::ObjectExpr(std::initializer_list<KV> Properties) {
for (const auto &P : Properties)
emplace(std::move(P.K), std::move(P.V));
}
// Give Expr::{Object,Array} more convenient names for literal use.
-using obj = Expr::Object;
-using ary = Expr::Array;
+using obj = Expr::ObjectExpr;
+using ary = Expr::ArrayExpr;
+
+llvm::Expected<Expr> parse(llvm::StringRef JSON);
+
+class ParseError : public llvm::ErrorInfo<ParseError> {
+ const char *Msg;
+ unsigned Line, Column, Offset;
+
+public:
+ static char ID;
+ ParseError(const char *Msg, unsigned Line, unsigned Column, unsigned Offset)
+ : Msg(Msg), Line(Line), Column(Column), Offset(Offset) {}
+ void log(llvm::raw_ostream &OS) const override {
+ OS << llvm::formatv("[{0}:{1}, byte={2}]: {3}", Line, Column, Offset, Msg);
+ }
+ std::error_code convertToErrorCode() const override {
+ return llvm::inconvertibleErrorCode();
+ }
+};
} // namespace json
} // namespace clangd
OpenPOWER on IntegriCloud