llvm-project/clang-tools-extra/clangd/JSONExpr.h

580 lines
17 KiB
C++

//===--- 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 {
// 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;
}
// 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