forked from OSchip/llvm-project
[JSON] Allow emitting comments in json::OStream
This isn't standard JSON, but is a popular extension. It will be used to show errors in context, rendering pseudo-json for humans. Differential Revision: https://reviews.llvm.org/D88103
This commit is contained in:
parent
3f739f736b
commit
140b7b6f09
|
@ -706,6 +706,7 @@ public:
|
||||||
/// json::OStream allows writing well-formed JSON without materializing
|
/// json::OStream allows writing well-formed JSON without materializing
|
||||||
/// all structures as json::Value ahead of time.
|
/// all structures as json::Value ahead of time.
|
||||||
/// It's faster, lower-level, and less safe than OS << json::Value.
|
/// It's faster, lower-level, and less safe than OS << json::Value.
|
||||||
|
/// It also allows emitting more constructs, such as comments.
|
||||||
///
|
///
|
||||||
/// Only one "top-level" object can be written to a stream.
|
/// Only one "top-level" object can be written to a stream.
|
||||||
/// Simplest usage involves passing lambdas (Blocks) to fill in containers:
|
/// Simplest usage involves passing lambdas (Blocks) to fill in containers:
|
||||||
|
@ -791,6 +792,10 @@ class OStream {
|
||||||
Contents();
|
Contents();
|
||||||
objectEnd();
|
objectEnd();
|
||||||
}
|
}
|
||||||
|
/// Emit a JavaScript comment associated with the next printed value.
|
||||||
|
/// The string must be valid until the next attribute or value is emitted.
|
||||||
|
/// Comments are not part of standard JSON, and many parsers reject them!
|
||||||
|
void comment(llvm::StringRef);
|
||||||
|
|
||||||
// High level functions to output object attributes.
|
// High level functions to output object attributes.
|
||||||
// Valid only within an object (any number of times).
|
// Valid only within an object (any number of times).
|
||||||
|
@ -826,6 +831,7 @@ class OStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
void valueBegin();
|
void valueBegin();
|
||||||
|
void flushComment();
|
||||||
void newline();
|
void newline();
|
||||||
|
|
||||||
enum Context {
|
enum Context {
|
||||||
|
@ -838,6 +844,7 @@ class OStream {
|
||||||
bool HasValue = false;
|
bool HasValue = false;
|
||||||
};
|
};
|
||||||
llvm::SmallVector<State, 16> Stack; // Never empty.
|
llvm::SmallVector<State, 16> Stack; // Never empty.
|
||||||
|
llvm::StringRef PendingComment;
|
||||||
llvm::raw_ostream &OS;
|
llvm::raw_ostream &OS;
|
||||||
unsigned IndentSize;
|
unsigned IndentSize;
|
||||||
unsigned Indent = 0;
|
unsigned Indent = 0;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "llvm/Support/JSON.h"
|
#include "llvm/Support/JSON.h"
|
||||||
#include "llvm/Support/ConvertUTF.h"
|
#include "llvm/Support/ConvertUTF.h"
|
||||||
#include "llvm/Support/Format.h"
|
#include "llvm/Support/Format.h"
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
|
@ -633,9 +634,40 @@ void llvm::json::OStream::valueBegin() {
|
||||||
}
|
}
|
||||||
if (Stack.back().Ctx == Array)
|
if (Stack.back().Ctx == Array)
|
||||||
newline();
|
newline();
|
||||||
|
flushComment();
|
||||||
Stack.back().HasValue = true;
|
Stack.back().HasValue = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OStream::comment(llvm::StringRef Comment) {
|
||||||
|
assert(PendingComment.empty() && "Only one comment per value!");
|
||||||
|
PendingComment = Comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OStream::flushComment() {
|
||||||
|
if (PendingComment.empty())
|
||||||
|
return;
|
||||||
|
OS << (IndentSize ? "/* " : "/*");
|
||||||
|
// Be sure not to accidentally emit "*/". Transform to "* /".
|
||||||
|
while (!PendingComment.empty()) {
|
||||||
|
auto Pos = PendingComment.find("*/");
|
||||||
|
if (Pos == StringRef::npos) {
|
||||||
|
OS << PendingComment;
|
||||||
|
PendingComment = "";
|
||||||
|
} else {
|
||||||
|
OS << PendingComment.take_front(Pos) << "* /";
|
||||||
|
PendingComment = PendingComment.drop_front(Pos + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OS << (IndentSize ? " */" : "*/");
|
||||||
|
// Comments are on their own line unless attached to an attribute value.
|
||||||
|
if (Stack.size() > 1 && Stack.back().Ctx == Singleton) {
|
||||||
|
if (IndentSize)
|
||||||
|
OS << ' ';
|
||||||
|
} else {
|
||||||
|
newline();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void llvm::json::OStream::newline() {
|
void llvm::json::OStream::newline() {
|
||||||
if (IndentSize) {
|
if (IndentSize) {
|
||||||
OS.write('\n');
|
OS.write('\n');
|
||||||
|
@ -657,6 +689,7 @@ void llvm::json::OStream::arrayEnd() {
|
||||||
if (Stack.back().HasValue)
|
if (Stack.back().HasValue)
|
||||||
newline();
|
newline();
|
||||||
OS << ']';
|
OS << ']';
|
||||||
|
assert(PendingComment.empty());
|
||||||
Stack.pop_back();
|
Stack.pop_back();
|
||||||
assert(!Stack.empty());
|
assert(!Stack.empty());
|
||||||
}
|
}
|
||||||
|
@ -675,6 +708,7 @@ void llvm::json::OStream::objectEnd() {
|
||||||
if (Stack.back().HasValue)
|
if (Stack.back().HasValue)
|
||||||
newline();
|
newline();
|
||||||
OS << '}';
|
OS << '}';
|
||||||
|
assert(PendingComment.empty());
|
||||||
Stack.pop_back();
|
Stack.pop_back();
|
||||||
assert(!Stack.empty());
|
assert(!Stack.empty());
|
||||||
}
|
}
|
||||||
|
@ -684,6 +718,7 @@ void llvm::json::OStream::attributeBegin(llvm::StringRef Key) {
|
||||||
if (Stack.back().HasValue)
|
if (Stack.back().HasValue)
|
||||||
OS << ',';
|
OS << ',';
|
||||||
newline();
|
newline();
|
||||||
|
flushComment();
|
||||||
Stack.back().HasValue = true;
|
Stack.back().HasValue = true;
|
||||||
Stack.emplace_back();
|
Stack.emplace_back();
|
||||||
Stack.back().Ctx = Singleton;
|
Stack.back().Ctx = Singleton;
|
||||||
|
@ -701,6 +736,7 @@ void llvm::json::OStream::attributeBegin(llvm::StringRef Key) {
|
||||||
void llvm::json::OStream::attributeEnd() {
|
void llvm::json::OStream::attributeEnd() {
|
||||||
assert(Stack.back().Ctx == Singleton);
|
assert(Stack.back().Ctx == Singleton);
|
||||||
assert(Stack.back().HasValue && "Attribute must have a value");
|
assert(Stack.back().HasValue && "Attribute must have a value");
|
||||||
|
assert(PendingComment.empty());
|
||||||
Stack.pop_back();
|
Stack.pop_back();
|
||||||
assert(Stack.back().Ctx == Object);
|
assert(Stack.back().Ctx == Object);
|
||||||
}
|
}
|
||||||
|
|
|
@ -420,15 +420,19 @@ TEST(JSONTest, Stream) {
|
||||||
std::string S;
|
std::string S;
|
||||||
llvm::raw_string_ostream OS(S);
|
llvm::raw_string_ostream OS(S);
|
||||||
OStream J(OS, Indent);
|
OStream J(OS, Indent);
|
||||||
|
J.comment("top*/level");
|
||||||
J.object([&] {
|
J.object([&] {
|
||||||
J.attributeArray("foo", [&] {
|
J.attributeArray("foo", [&] {
|
||||||
J.value(nullptr);
|
J.value(nullptr);
|
||||||
|
J.comment("element");
|
||||||
J.value(42.5);
|
J.value(42.5);
|
||||||
J.arrayBegin();
|
J.arrayBegin();
|
||||||
J.value(43);
|
J.value(43);
|
||||||
J.arrayEnd();
|
J.arrayEnd();
|
||||||
});
|
});
|
||||||
|
J.comment("attribute");
|
||||||
J.attributeBegin("bar");
|
J.attributeBegin("bar");
|
||||||
|
J.comment("attribute value");
|
||||||
J.objectBegin();
|
J.objectBegin();
|
||||||
J.objectEnd();
|
J.objectEnd();
|
||||||
J.attributeEnd();
|
J.attributeEnd();
|
||||||
|
@ -437,17 +441,21 @@ TEST(JSONTest, Stream) {
|
||||||
return OS.str();
|
return OS.str();
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *Plain = R"({"foo":[null,42.5,[43]],"bar":{},"baz":"xyz"})";
|
const char *Plain =
|
||||||
|
R"(/*top* /level*/{"foo":[null,/*element*/42.5,[43]],/*attribute*/"bar":/*attribute value*/{},"baz":"xyz"})";
|
||||||
EXPECT_EQ(Plain, StreamStuff(0));
|
EXPECT_EQ(Plain, StreamStuff(0));
|
||||||
const char *Pretty = R"({
|
const char *Pretty = R"(/* top* /level */
|
||||||
|
{
|
||||||
"foo": [
|
"foo": [
|
||||||
null,
|
null,
|
||||||
|
/* element */
|
||||||
42.5,
|
42.5,
|
||||||
[
|
[
|
||||||
43
|
43
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"bar": {},
|
/* attribute */
|
||||||
|
"bar": /* attribute value */ {},
|
||||||
"baz": "xyz"
|
"baz": "xyz"
|
||||||
})";
|
})";
|
||||||
EXPECT_EQ(Pretty, StreamStuff(2));
|
EXPECT_EQ(Pretty, StreamStuff(2));
|
||||||
|
|
Loading…
Reference in New Issue