forked from OSchip/llvm-project
580 lines
17 KiB
C++
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
|