//===--- JSONExpr.h - JSON expressions, parsing and serialization - C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===---------------------------------------------------------------------===// // 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 #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" #include namespace clang { namespace clangd { namespace json { // An Expr is an JSON value of unknown type. // They can be copied, but should generally be moved. // // === Composing expressions === // // You can implicitly construct Exprs from: // - strings: std::string, SmallString, formatv, StringRef, char* // (char*, and StringRef are references, not copies!) // - numbers // - booleans // - null: nullptr // - arrays: {"foo", 42.0, false} // - serializable things: types with toJSON(const T&)->Expr, found by ADL // // They can also be constructed from object/array helpers: // - json::obj is a type like map // - json::ary is a type like vector // 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 S = E.asString() // assert(E.kind() == Expr::String); // // Array and Object also have typed indexing accessors for easy traversal: // Expected E = parse(R"( {"options": {"font": "sans-serif"}} )"); // if (json::obj* O = E->asObject()) // if (json::obj* Opts = O->getObject("options")) // if (Optional Font = Opts->getString("font")) // assert(Opts->at("font").kind() == Expr::String); // // === Converting expressions to objects === // // The convention is to have a deserializer function findable via ADL: // fromJSON(const json::Expr&, T&)->bool // Deserializers are provided for: // - bool // - int // - double // - std::string // - vector, where T is deserializable // - map, where T is deserializable // - Optional, where T is deserializable // // ObjectMapper can help writing fromJSON() functions for object types: // bool fromJSON(const Expr &E, MyStruct &R) { // ObjectMapper O(E); // if (!O || !O.map("mandatory_field", R.MandatoryField)) // return false; // O.map("optional_field", R.OptionalField); // return true; // } // // === 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 E = json::parse("[1, 2, null]"); // assert(E && E->kind() == Expr::Array); class Expr { public: enum Kind { Null, Boolean, Number, String, Array, Object, }; class ObjectExpr; class ObjectKey; 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 Elements) : Expr(ArrayExpr(Elements)) {} Expr(ArrayExpr &&Elements) : Type(T_Array) { create(std::move(Elements)); } Expr(ObjectExpr &&Properties) : Type(T_Object) { create(std::move(Properties)); } // Strings: types with value semantics. Expr(std::string &&V) : Type(T_String) { create(std::move(V)); } Expr(const std::string &V) : Type(T_String) { create(V); } Expr(const llvm::SmallVectorImpl &V) : Type(T_String) { create(V.begin(), V.end()); } Expr(const llvm::formatv_object_base &V) : Expr(V.str()){}; // Strings: types with reference semantics. Expr(llvm::StringRef V) : Type(T_StringRef) { create(V); } Expr(const char *V) : Type(T_StringRef) { create(V); } Expr(std::nullptr_t) : Type(T_Null) {} // Prevent implicit conversions to boolean. template ::value>::type> Expr(T B) : Type(T_Boolean) { create(B); } // Numbers: arithmetic types that are not boolean. template < typename T, typename = typename std::enable_if::value>::type, typename = typename std::enable_if::value>::value>::type> Expr(T D) : Type(T_Number) { create(D); } // Types with a toJSON(const T&)->Expr function, found by ADL. template ::value>> Expr(const T &V) : Expr(toJSON(V)) {} Expr &operator=(const Expr &M) { destroy(); copyFrom(M); return *this; } Expr &operator=(Expr &&M) { destroy(); moveFrom(std::move(M)); return *this; } ~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; } llvm_unreachable("Unknown kind"); } // Typed accessors return None/nullptr if the Expr is not of this type. llvm::Optional asNull() const { if (LLVM_LIKELY(Type == T_Null)) return nullptr; return llvm::None; } llvm::Optional asBoolean() const { if (LLVM_LIKELY(Type == T_Boolean)) return as(); return llvm::None; } llvm::Optional asNumber() const { if (LLVM_LIKELY(Type == T_Number)) return as(); return llvm::None; } llvm::Optional asInteger() const { if (LLVM_LIKELY(Type == T_Number)) { double D = as(); if (LLVM_LIKELY(std::modf(D, &D) == 0 && D >= std::numeric_limits::min() && D <= std::numeric_limits::max())) return D; } return llvm::None; } llvm::Optional asString() const { if (Type == T_String) return llvm::StringRef(as()); if (LLVM_LIKELY(Type == T_StringRef)) return as(); return llvm::None; } const ObjectExpr *asObject() const { return LLVM_LIKELY(Type == T_Object) ? &as() : nullptr; } ObjectExpr *asObject() { return LLVM_LIKELY(Type == T_Object) ? &as() : nullptr; } const ArrayExpr *asArray() const { return LLVM_LIKELY(Type == T_Array) ? &as() : nullptr; } ArrayExpr *asArray() { return LLVM_LIKELY(Type == T_Array) ? &as() : nullptr; } friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Expr &); private: void destroy(); void copyFrom(const Expr &M); // We allow moving from *const* Exprs, by marking all members as mutable! // This hack is needed to support initializer-list syntax efficiently. // (std::initializer_list is a container of const T). void moveFrom(const Expr &&M); template void create(U &&... V) { new (&as()) T(std::forward(V)...); } template T &as() const { return *reinterpret_cast(Union.buffer); } template void print(llvm::raw_ostream &, const Indenter &) const; friend struct llvm::format_provider; enum ExprType : char { T_Null, T_Boolean, T_Number, T_StringRef, T_String, T_Object, T_Array, }; mutable ExprType Type; public: // ObjectKey is a used to capture keys in Expr::ObjectExpr. Like Expr but: // - only strings are allowed // - it's optimized for the string literal case (Owned == nullptr) class ObjectKey { public: ObjectKey(const char *S) : Data(S) {} ObjectKey(llvm::StringRef S) : Data(S) {} ObjectKey(std::string &&V) : Owned(new std::string(std::move(V))), Data(*Owned) {} ObjectKey(const std::string &V) : Owned(new std::string(V)), Data(*Owned) {} ObjectKey(const llvm::SmallVectorImpl &V) : ObjectKey(std::string(V.begin(), V.end())) {} ObjectKey(const llvm::formatv_object_base &V) : ObjectKey(V.str()) {} ObjectKey(const ObjectKey &C) { *this = C; } ObjectKey(ObjectKey &&C) : ObjectKey(static_cast(C)) {} ObjectKey &operator=(const ObjectKey &C) { if (C.Owned) { Owned.reset(new std::string(*C.Owned)); Data = *Owned; } else { Data = C.Data; } return *this; } ObjectKey &operator=(ObjectKey &&) = default; operator llvm::StringRef() const { return Data; } friend bool operator<(const ObjectKey &L, const ObjectKey &R) { return L.Data < R.Data; } // "cheating" move-constructor for moving from initializer_list. ObjectKey(const ObjectKey &&V) { Owned = std::move(V.Owned); Data = V.Data; } private: mutable std::unique_ptr Owned; // mutable for cheating. llvm::StringRef Data; }; class ObjectExpr : public std::map { public: explicit ObjectExpr() {} // Use a custom struct for list-init, because pair forces extra copies. struct KV; explicit ObjectExpr(std::initializer_list Properties); // Allow [] as if Expr was default-constructible as null. Expr &operator[](const ObjectKey &K) { return emplace(K, Expr(nullptr)).first->second; } 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 getNull(const ObjectKey &K) const { if (auto *V = get(K)) return V->asNull(); return llvm::None; } llvm::Optional getBoolean(const ObjectKey &K) const { if (auto *V = get(K)) return V->asBoolean(); return llvm::None; } llvm::Optional getNumber(const ObjectKey &K) const { if (auto *V = get(K)) return V->asNumber(); return llvm::None; } llvm::Optional getInteger(const ObjectKey &K) const { if (auto *V = get(K)) return V->asInteger(); return llvm::None; } llvm::Optional 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 { public: explicit ArrayExpr() {} explicit ArrayExpr(std::initializer_list Elements) { reserve(Elements.size()); for (const Expr &V : Elements) emplace_back(std::move(V)); }; template explicit ArrayExpr(const Collection &C) { for (const auto &V : C) emplace_back(V); } // Typed accessors return None/nullptr if the element has the wrong type. llvm::Optional getNull(size_t I) const { return (*this)[I].asNull(); } llvm::Optional getBoolean(size_t I) const { return (*this)[I].asBoolean(); } llvm::Optional getNumber(size_t I) const { return (*this)[I].asNumber(); } llvm::Optional getInteger(size_t I) const { return (*this)[I].asInteger(); } llvm::Optional 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: mutable llvm::AlignedCharArrayUnion Union; }; 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::ObjectExpr::ObjectExpr(std::initializer_list 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::ObjectExpr; using ary = Expr::ArrayExpr; // Standard deserializers. inline bool fromJSON(const json::Expr &E, std::string &Out) { if (auto S = E.asString()) { Out = *S; return true; } return false; } inline bool fromJSON(const json::Expr &E, int &Out) { if (auto S = E.asInteger()) { Out = *S; return true; } return false; } inline bool fromJSON(const json::Expr &E, double &Out) { if (auto S = E.asNumber()) { Out = *S; return true; } return false; } inline bool fromJSON(const json::Expr &E, bool &Out) { if (auto S = E.asBoolean()) { Out = *S; return true; } return false; } template bool fromJSON(const json::Expr &E, llvm::Optional &Out) { if (E.asNull()) { Out = llvm::None; return true; } T Result; if (!fromJSON(E, Result)) return false; Out = std::move(Result); return true; } template bool fromJSON(const json::Expr &E, std::vector &Out) { if (auto *A = E.asArray()) { Out.clear(); Out.resize(A->size()); for (size_t I = 0; I < A->size(); ++I) if (!fromJSON((*A)[I], Out[I])) return false; return true; } return false; } template bool fromJSON(const json::Expr &E, std::map &Out) { if (auto *O = E.asObject()) { Out.clear(); for (const auto &KV : *O) if (!fromJSON(KV.second, Out[llvm::StringRef(KV.first)])) return false; return true; } return false; } // Helper for mapping JSON objects onto protocol structs. // See file header for example. class ObjectMapper { public: ObjectMapper(const json::Expr &E) : O(E.asObject()) {} // True if the expression is an object. // Must be checked before calling map(). operator bool() { return O; } // Maps a property to a field, if it exists. template bool map(const char *Prop, T &Out) { assert(*this && "Must check this is an object before calling map()"); if (const json::Expr *E = O->get(Prop)) return fromJSON(*E, Out); return false; } // Optional requires special handling, because missing keys are OK. template bool map(const char *Prop, llvm::Optional &Out) { assert(*this && "Must check this is an object before calling map()"); if (const json::Expr *E = O->get(Prop)) return fromJSON(*E, Out); Out = llvm::None; return true; } private: const json::obj *O; }; llvm::Expected parse(llvm::StringRef JSON); class ParseError : public llvm::ErrorInfo { 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 } // namespace clang namespace llvm { template <> struct format_provider { static void format(const clang::clangd::json::Expr &, raw_ostream &, StringRef); }; } // namespace llvm #endif