| //===--- 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 <map> |
| |
| namespace clang { |
| namespace clangd { |
| namespace json { |
| class Expr; |
| template <typename T> Expr toJSON(const llvm::Optional<T> &Opt); |
| |
| // 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<StringExpr, Expr> |
| // - json::ary is a type like vector<Expr> |
| // 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.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->asObject()) |
| // if (json::obj* Opts = O->getObject("options")) |
| // if (Optional<StringRef> 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<T>, where T is deserializable |
| // - map<string, T>, where T is deserializable |
| // - Optional<T>, 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<Expr> 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<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)); } |
| Expr(const std::string &V) : Type(T_String) { create<std::string>(V); } |
| Expr(const llvm::SmallVectorImpl<char> &V) : Type(T_String) { |
| create<std::string>(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<llvm::StringRef>(V); } |
| Expr(const char *V) : Type(T_StringRef) { create<llvm::StringRef>(V); } |
| Expr(std::nullptr_t) : Type(T_Null) {} |
| // Prevent implicit conversions to boolean. |
| template <typename T, typename = typename std::enable_if< |
| std::is_same<T, bool>::value>::type> |
| Expr(T B) : Type(T_Boolean) { |
| create<bool>(B); |
| } |
| // Numbers: arithmetic types that are not boolean. |
| template < |
| typename T, |
| typename = typename std::enable_if<std::is_arithmetic<T>::value>::type, |
| typename = typename std::enable_if<std::integral_constant< |
| bool, !std::is_same<T, bool>::value>::value>::type> |
| Expr(T D) : Type(T_Number) { |
| create<double>(D); |
| } |
| // Types with a toJSON(const T&)->Expr function, found by ADL. |
| template <typename T, |
| typename = typename std::enable_if<std::is_same< |
| Expr, decltype(toJSON(*(const T *)nullptr))>::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<std::nullptr_t> asNull() const { |
| if (LLVM_LIKELY(Type == T_Null)) |
| return nullptr; |
| return llvm::None; |
| } |
| llvm::Optional<bool> asBoolean() const { |
| if (LLVM_LIKELY(Type == T_Boolean)) |
| return as<bool>(); |
| return llvm::None; |
| } |
| llvm::Optional<double> asNumber() const { |
| if (LLVM_LIKELY(Type == T_Number)) |
| return as<double>(); |
| return llvm::None; |
| } |
| llvm::Optional<int64_t> asInteger() const { |
| if (LLVM_LIKELY(Type == T_Number)) { |
| double D = as<double>(); |
| if (LLVM_LIKELY(std::modf(D, &D) == 0 && |
| D >= std::numeric_limits<int64_t>::min() && |
| D <= std::numeric_limits<int64_t>::max())) |
| return D; |
| } |
| return llvm::None; |
| } |
| 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 *asObject() const { |
| return LLVM_LIKELY(Type == T_Object) ? &as<ObjectExpr>() : nullptr; |
| } |
| ObjectExpr *asObject() { |
| return LLVM_LIKELY(Type == T_Object) ? &as<ObjectExpr>() : nullptr; |
| } |
| const ArrayExpr *asArray() const { |
| return LLVM_LIKELY(Type == T_Array) ? &as<ArrayExpr>() : nullptr; |
| } |
| ArrayExpr *asArray() { |
| return LLVM_LIKELY(Type == T_Array) ? &as<ArrayExpr>() : 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<T> is a container of const T). |
| void moveFrom(const Expr &&M); |
| |
| template <typename T, typename... U> void create(U &&... V) { |
| new (&as<T>()) T(std::forward<U>(V)...); |
| } |
| template <typename T> T &as() const { |
| return *reinterpret_cast<T *>(Union.buffer); |
| } |
| |
| template <typename Indenter> |
| void print(llvm::raw_ostream &, const Indenter &) const; |
| friend struct llvm::format_provider<clang::clangd::json::Expr>; |
| |
| 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<char> &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<const ObjectKey &&>(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<std::string> Owned; // mutable for cheating. |
| llvm::StringRef Data; |
| }; |
| |
| class ObjectExpr : public std::map<ObjectKey, Expr> { |
| public: |
| explicit ObjectExpr() {} |
| // Use a custom struct for list-init, because pair forces extra copies. |
| struct KV; |
| explicit ObjectExpr(std::initializer_list<KV> 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<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<int64_t> getInteger(const ObjectKey &K) const { |
| if (auto *V = get(K)) |
| return V->asInteger(); |
| 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> { |
| public: |
| 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 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<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<int64_t> getInteger(size_t I) const { |
| return (*this)[I].asInteger(); |
| } |
| 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: |
| mutable llvm::AlignedCharArrayUnion<bool, double, llvm::StringRef, |
| std::string, ArrayExpr, ObjectExpr> |
| 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<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::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 <typename T> |
| bool fromJSON(const json::Expr &E, llvm::Optional<T> &Out) { |
| if (E.asNull()) { |
| Out = llvm::None; |
| return true; |
| } |
| T Result; |
| if (!fromJSON(E, Result)) |
| return false; |
| Out = std::move(Result); |
| return true; |
| } |
| template <typename T> bool fromJSON(const json::Expr &E, std::vector<T> &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 <typename T> |
| bool fromJSON(const json::Expr &E, std::map<std::string, T> &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; |
| } |
| |
| template <typename T> |
| json::Expr toJSON(const llvm::Optional<T>& Opt) { |
| return Opt ? json::Expr(*Opt) : json::Expr(nullptr); |
| } |
| |
| // 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 <typename T> 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 <typename T> bool map(const char *Prop, llvm::Optional<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); |
| Out = llvm::None; |
| return true; |
| } |
| |
| private: |
| const json::obj *O; |
| }; |
| |
| 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 |
| } // namespace clang |
| |
| namespace llvm { |
| template <> struct format_provider<clang::clangd::json::Expr> { |
| static void format(const clang::clangd::json::Expr &, raw_ostream &, |
| StringRef); |
| }; |
| } // namespace llvm |
| |
| #endif |