Skip to content

Commit

Permalink
YAML: Add an optional 'flow' field to the mapping trait to allow flow…
Browse files Browse the repository at this point in the history
… mapping output.

This patch adds an optional 'flow' field to the MappingTrait
class so that yaml IO will be able to output flow mappings.

Reviewers: Justin Bogner

Differential Revision: http://reviews.llvm.org/D9450

llvm-svn: 236456
  • Loading branch information
hyp committed May 4, 2015
1 parent d71a177 commit b122508
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 11 deletions.
25 changes: 25 additions & 0 deletions llvm/docs/YamlIO.rst
Expand Up @@ -723,6 +723,31 @@ because it is a programming error to have invalid struct values.
}
};

Flow Mapping
------------
A YAML "flow mapping" is a mapping that uses the inline notation
(e.g { x: 1, y: 0 } ) when written to YAML. To specify that a type should be
written in YAML using flow mapping, your MappingTraits specialization should
add "static const bool flow = true;". For instance:

.. code-block:: c++

using llvm::yaml::MappingTraits;
using llvm::yaml::IO;

struct Stuff {
...
};

template <>
struct MappingTraits<Stuff> {
static void mapping(IO &io, Stuff &stuff) {
...
}

static const bool flow = true;
}


Sequence
========
Expand Down
46 changes: 39 additions & 7 deletions llvm/include/llvm/Support/YAMLTraits.h
Expand Up @@ -46,6 +46,10 @@ struct MappingTraits {
// static void mapping(IO &io, T &fields);
// Optionally may provide:
// static StringRef validate(IO &io, T &fields);
//
// The optional flow flag will cause generated YAML to use a flow mapping
// (e.g. { a: 0, b: 1 }):
// static const bool flow = true;
};


Expand Down Expand Up @@ -445,6 +449,9 @@ class IO {
virtual bool preflightKey(const char*, bool, bool, bool &, void *&) = 0;
virtual void postflightKey(void*) = 0;

virtual void beginFlowMapping() = 0;
virtual void endFlowMapping() = 0;

virtual void beginEnumScalar() = 0;
virtual bool matchEnumScalar(const char*, bool) = 0;
virtual bool matchEnumFallback() = 0;
Expand Down Expand Up @@ -643,7 +650,10 @@ yamlize(IO &io, T &Val, bool) {
template<typename T>
typename std::enable_if<validatedMappingTraits<T>::value, void>::type
yamlize(IO &io, T &Val, bool) {
io.beginMapping();
if (has_FlowTraits<MappingTraits<T>>::value)
io.beginFlowMapping();
else
io.beginMapping();
if (io.outputting()) {
StringRef Err = MappingTraits<T>::validate(io, Val);
if (!Err.empty()) {
Expand All @@ -657,15 +667,24 @@ yamlize(IO &io, T &Val, bool) {
if (!Err.empty())
io.setError(Err);
}
io.endMapping();
if (has_FlowTraits<MappingTraits<T>>::value)
io.endFlowMapping();
else
io.endMapping();
}

template<typename T>
typename std::enable_if<unvalidatedMappingTraits<T>::value, void>::type
yamlize(IO &io, T &Val, bool) {
io.beginMapping();
MappingTraits<T>::mapping(io, Val);
io.endMapping();
if (has_FlowTraits<MappingTraits<T>>::value) {
io.beginFlowMapping();
MappingTraits<T>::mapping(io, Val);
io.endFlowMapping();
} else {
io.beginMapping();
MappingTraits<T>::mapping(io, Val);
io.endMapping();
}
}

template<typename T>
Expand Down Expand Up @@ -900,6 +919,8 @@ class Input : public IO {
void endMapping() override;
bool preflightKey(const char *, bool, bool, bool &, void *&) override;
void postflightKey(void *) override;
void beginFlowMapping() override;
void endFlowMapping() override;
unsigned beginSequence() override;
void endSequence() override;
bool preflightElement(unsigned index, void *&) override;
Expand Down Expand Up @@ -1028,6 +1049,8 @@ class Output : public IO {
void endMapping() override;
bool preflightKey(const char *key, bool, bool, bool &, void *&) override;
void postflightKey(void *) override;
void beginFlowMapping() override;
void endFlowMapping() override;
unsigned beginSequence() override;
void endSequence() override;
bool preflightElement(unsigned, void *&) override;
Expand Down Expand Up @@ -1060,13 +1083,22 @@ class Output : public IO {
void newLineCheck();
void outputNewLine();
void paddedKey(StringRef key);

enum InState { inSeq, inFlowSeq, inMapFirstKey, inMapOtherKey };
void flowKey(StringRef Key);

enum InState {
inSeq,
inFlowSeq,
inMapFirstKey,
inMapOtherKey,
inFlowMapFirstKey,
inFlowMapOtherKey
};

llvm::raw_ostream &Out;
SmallVector<InState, 8> StateStack;
int Column;
int ColumnAtFlowStart;
int ColumnAtMapFlowStart;
bool NeedBitValueComma;
bool NeedFlowSequenceComma;
bool EnumerationMatchFound;
Expand Down
50 changes: 46 additions & 4 deletions llvm/lib/Support/YAMLTraits.cpp
Expand Up @@ -168,6 +168,10 @@ void Input::endMapping() {
}
}

void Input::beginFlowMapping() { beginMapping(); }

void Input::endFlowMapping() { endMapping(); }

unsigned Input::beginSequence() {
if (SequenceHNode *SQ = dyn_cast<SequenceHNode>(CurrentNode))
return SQ->Entries.size();
Expand Down Expand Up @@ -393,6 +397,7 @@ Output::Output(raw_ostream &yout, void *context)
Out(yout),
Column(0),
ColumnAtFlowStart(0),
ColumnAtMapFlowStart(0),
NeedBitValueComma(false),
NeedFlowSequenceComma(false),
EnumerationMatchFound(false),
Expand Down Expand Up @@ -427,8 +432,13 @@ bool Output::preflightKey(const char *Key, bool Required, bool SameAsDefault,
bool &UseDefault, void *&) {
UseDefault = false;
if (Required || !SameAsDefault) {
this->newLineCheck();
this->paddedKey(Key);
auto State = StateStack.back();
if (State == inFlowMapFirstKey || State == inFlowMapOtherKey) {
flowKey(Key);
} else {
this->newLineCheck();
this->paddedKey(Key);
}
return true;
}
return false;
Expand All @@ -438,9 +448,24 @@ void Output::postflightKey(void *) {
if (StateStack.back() == inMapFirstKey) {
StateStack.pop_back();
StateStack.push_back(inMapOtherKey);
} else if (StateStack.back() == inFlowMapFirstKey) {
StateStack.pop_back();
StateStack.push_back(inFlowMapOtherKey);
}
}

void Output::beginFlowMapping() {
StateStack.push_back(inFlowMapFirstKey);
this->newLineCheck();
ColumnAtMapFlowStart = Column;
output("{ ");
}

void Output::endFlowMapping() {
StateStack.pop_back();
this->outputUpToEndOfLine(" }");
}

void Output::beginDocuments() {
this->outputUpToEndOfLine("---");
}
Expand Down Expand Up @@ -607,7 +632,9 @@ void Output::output(StringRef s) {

void Output::outputUpToEndOfLine(StringRef s) {
this->output(s);
if (StateStack.empty() || StateStack.back() != inFlowSeq)
if (StateStack.empty() || (StateStack.back() != inFlowSeq &&
StateStack.back() != inFlowMapFirstKey &&
StateStack.back() != inFlowMapOtherKey))
NeedsNewLine = true;
}

Expand All @@ -634,7 +661,8 @@ void Output::newLineCheck() {
if (StateStack.back() == inSeq) {
OutputDash = true;
} else if ((StateStack.size() > 1) && ((StateStack.back() == inMapFirstKey) ||
(StateStack.back() == inFlowSeq)) &&
(StateStack.back() == inFlowSeq) ||
(StateStack.back() == inFlowMapFirstKey)) &&
(StateStack[StateStack.size() - 2] == inSeq)) {
--Indent;
OutputDash = true;
Expand All @@ -659,6 +687,20 @@ void Output::paddedKey(StringRef key) {
output(" ");
}

void Output::flowKey(StringRef Key) {
if (StateStack.back() == inFlowMapOtherKey)
output(", ");
if (Column > 70) {
output("\n");
for (int I = 0; I < ColumnAtMapFlowStart; ++I)
output(" ");
Column = ColumnAtMapFlowStart;
output(" ");
}
output(Key);
output(": ");
}

//===----------------------------------------------------------------------===//
// traits for built-in types
//===----------------------------------------------------------------------===//
Expand Down
85 changes: 85 additions & 0 deletions llvm/unittests/Support/YAMLIOTest.cpp
Expand Up @@ -1367,6 +1367,91 @@ TEST(YAMLIO, TestValidatingInput) {
EXPECT_TRUE(!!yin.error());
}

//===----------------------------------------------------------------------===//
// Test flow mapping
//===----------------------------------------------------------------------===//

struct FlowFooBar {
int foo;
int bar;

FlowFooBar() : foo(0), bar(0) {}
FlowFooBar(int foo, int bar) : foo(foo), bar(bar) {}
};

typedef std::vector<FlowFooBar> FlowFooBarSequence;

LLVM_YAML_IS_SEQUENCE_VECTOR(FlowFooBar)

struct FlowFooBarDoc {
FlowFooBar attribute;
FlowFooBarSequence seq;
};

namespace llvm {
namespace yaml {
template <>
struct MappingTraits<FlowFooBar> {
static void mapping(IO &io, FlowFooBar &fb) {
io.mapRequired("foo", fb.foo);
io.mapRequired("bar", fb.bar);
}

static const bool flow = true;
};

template <>
struct MappingTraits<FlowFooBarDoc> {
static void mapping(IO &io, FlowFooBarDoc &fb) {
io.mapRequired("attribute", fb.attribute);
io.mapRequired("seq", fb.seq);
}
};
}
}

//
// Test writing then reading back custom mappings
//
TEST(YAMLIO, TestReadWriteMyFlowMapping) {
std::string intermediate;
{
FlowFooBarDoc doc;
doc.attribute = FlowFooBar(42, 907);
doc.seq.push_back(FlowFooBar(1, 2));
doc.seq.push_back(FlowFooBar(0, 0));
doc.seq.push_back(FlowFooBar(-1, 1024));

llvm::raw_string_ostream ostr(intermediate);
Output yout(ostr);
yout << doc;

// Verify that mappings were written in flow style
ostr.flush();
llvm::StringRef flowOut(intermediate);
EXPECT_NE(llvm::StringRef::npos, flowOut.find("{ foo: 42, bar: 907 }"));
EXPECT_NE(llvm::StringRef::npos, flowOut.find("- { foo: 1, bar: 2 }"));
EXPECT_NE(llvm::StringRef::npos, flowOut.find("- { foo: 0, bar: 0 }"));
EXPECT_NE(llvm::StringRef::npos, flowOut.find("- { foo: -1, bar: 1024 }"));
}

{
Input yin(intermediate);
FlowFooBarDoc doc2;
yin >> doc2;

EXPECT_FALSE(yin.error());
EXPECT_EQ(doc2.attribute.foo, 42);
EXPECT_EQ(doc2.attribute.bar, 907);
EXPECT_EQ(doc2.seq.size(), 3UL);
EXPECT_EQ(doc2.seq[0].foo, 1);
EXPECT_EQ(doc2.seq[0].bar, 2);
EXPECT_EQ(doc2.seq[1].foo, 0);
EXPECT_EQ(doc2.seq[1].bar, 0);
EXPECT_EQ(doc2.seq[2].foo, -1);
EXPECT_EQ(doc2.seq[2].bar, 1024);
}
}

//===----------------------------------------------------------------------===//
// Test error handling
Expand Down

0 comments on commit b122508

Please sign in to comment.