aboutsummaryrefslogtreecommitdiff
path: root/clangd/JSONExpr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clangd/JSONExpr.cpp')
-rw-r--r--clangd/JSONExpr.cpp221
1 files changed, 221 insertions, 0 deletions
diff --git a/clangd/JSONExpr.cpp b/clangd/JSONExpr.cpp
new file mode 100644
index 00000000..968892c2
--- /dev/null
+++ b/clangd/JSONExpr.cpp
@@ -0,0 +1,221 @@
+#include "JSONExpr.h"
+
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+namespace clang {
+namespace clangd {
+namespace json {
+
+void Expr::copyFrom(const Expr &M) {
+ Type = M.Type;
+ switch (Type) {
+ case T_Null:
+ case T_Boolean:
+ case T_Number:
+ memcpy(Union.buffer, M.Union.buffer, sizeof(Union.buffer));
+ break;
+ case T_StringRef:
+ create<StringRef>(M.as<StringRef>());
+ break;
+ case T_String:
+ create<std::string>(M.as<std::string>());
+ break;
+ case T_Object:
+ create<Object>(M.as<Object>());
+ break;
+ case T_Array:
+ create<Array>(M.as<Array>());
+ break;
+ }
+}
+
+void Expr::moveFrom(const Expr &&M) {
+ Type = M.Type;
+ switch (Type) {
+ case T_Null:
+ case T_Boolean:
+ case T_Number:
+ memcpy(Union.buffer, M.Union.buffer, sizeof(Union.buffer));
+ break;
+ case T_StringRef:
+ create<StringRef>(M.as<StringRef>());
+ break;
+ case T_String:
+ create<std::string>(std::move(M.as<std::string>()));
+ M.Type = T_Null;
+ break;
+ case T_Object:
+ create<Object>(std::move(M.as<Object>()));
+ M.Type = T_Null;
+ break;
+ case T_Array:
+ create<Array>(std::move(M.as<Array>()));
+ M.Type = T_Null;
+ break;
+ }
+}
+
+void Expr::destroy() {
+ switch (Type) {
+ case T_Null:
+ case T_Boolean:
+ case T_Number:
+ break;
+ case T_StringRef:
+ as<StringRef>().~StringRef();
+ break;
+ case T_String:
+ as<std::string>().~basic_string();
+ break;
+ case T_Object:
+ as<Object>().~Object();
+ break;
+ case T_Array:
+ as<Array>().~Array();
+ break;
+ }
+}
+
+} // namespace json
+} // namespace clangd
+} // namespace clang
+
+namespace {
+void quote(llvm::raw_ostream &OS, llvm::StringRef S) {
+ OS << '\"';
+ for (unsigned char C : S) {
+ if (C == 0x22 || C == 0x5C)
+ OS << '\\';
+ if (C >= 0x20) {
+ OS << C;
+ continue;
+ }
+ OS << '\\';
+ switch (C) {
+ // A few characters are common enough to make short escapes worthwhile.
+ case '\t':
+ OS << 't';
+ break;
+ case '\n':
+ OS << 'n';
+ break;
+ case '\r':
+ OS << 'r';
+ break;
+ default:
+ OS << 'u';
+ llvm::write_hex(OS, C, llvm::HexPrintStyle::Lower, 4);
+ break;
+ }
+ }
+ OS << '\"';
+}
+
+enum IndenterAction {
+ Indent,
+ Outdent,
+ Newline,
+ Space,
+};
+} // namespace
+
+// Prints JSON. The indenter can be used to control formatting.
+template <typename Indenter>
+void clang::clangd::json::Expr::print(raw_ostream &OS,
+ const Indenter &I) const {
+ switch (Type) {
+ case T_Null:
+ OS << "null";
+ break;
+ case T_Boolean:
+ OS << (as<bool>() ? "true" : "false");
+ break;
+ case T_Number:
+ OS << format("%g", as<double>());
+ break;
+ case T_StringRef:
+ quote(OS, as<StringRef>());
+ break;
+ case T_String:
+ quote(OS, as<std::string>());
+ break;
+ case T_Object: {
+ bool Comma = false;
+ OS << '{';
+ I(Indent);
+ for (const auto &P : as<Expr::Object>()) {
+ if (Comma)
+ OS << ',';
+ Comma = true;
+ I(Newline);
+ quote(OS, P.first);
+ OS << ':';
+ I(Space);
+ P.second.print(OS, I);
+ }
+ I(Outdent);
+ if (Comma)
+ I(Newline);
+ OS << '}';
+ break;
+ }
+ case T_Array: {
+ bool Comma = false;
+ OS << '[';
+ I(Indent);
+ for (const auto &E : as<Expr::Array>()) {
+ if (Comma)
+ OS << ',';
+ Comma = true;
+ I(Newline);
+ E.print(OS, I);
+ }
+ I(Outdent);
+ if (Comma)
+ I(Newline);
+ OS << ']';
+ break;
+ }
+ }
+}
+
+namespace clang {
+namespace clangd {
+namespace json {
+llvm::raw_ostream &operator<<(raw_ostream &OS, const Expr &E) {
+ E.print(OS, [](IndenterAction A) { /*ignore*/ });
+ return OS;
+}
+} // namespace json
+} // namespace clangd
+} // namespace clang
+
+void llvm::format_provider<clang::clangd::json::Expr>::format(
+ const clang::clangd::json::Expr &E, raw_ostream &OS, StringRef Options) {
+ if (Options.empty()) {
+ OS << E;
+ return;
+ }
+ unsigned IndentAmount = 0;
+ if (Options.getAsInteger(/*Radix=*/10, IndentAmount))
+ assert(false && "json::Expr format options should be an integer");
+ unsigned IndentLevel = 0;
+ E.print(OS, [&](IndenterAction A) {
+ switch (A) {
+ case Newline:
+ OS << '\n';
+ OS.indent(IndentLevel);
+ break;
+ case Space:
+ OS << ' ';
+ break;
+ case Indent:
+ IndentLevel += IndentAmount;
+ break;
+ case Outdent:
+ IndentLevel -= IndentAmount;
+ break;
+ };
+ });
+}