//===----------------------------------------------------------------------===//
#include "llvm/Support/YAMLParser.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Twine.h"
void Node::anchor() {}
void NullNode::anchor() {}
void ScalarNode::anchor() {}
+void BlockScalarNode::anchor() {}
void KeyValueNode::anchor() {}
void MappingNode::anchor() {}
void SequenceNode::anchor() {}
TK_Key,
TK_Value,
TK_Scalar,
+ TK_BlockScalar,
TK_Alias,
TK_Anchor,
TK_Tag
/// of the token in the input.
StringRef Range;
+ /// The value of a block scalar node.
+ std::string Value;
+
Token() : Kind(TK_Error) {}
};
}
Token *createNode(const Token &V) {
return new (Alloc.Allocate<Token>()) Token(V);
}
- static void deleteNode(Token *V) {}
+ static void deleteNode(Token *V) { V->~Token(); }
void addNodeToList(Token *) {}
void removeNodeFromList(Token *) {}
/// @brief Scans YAML tokens from a MemoryBuffer.
class Scanner {
public:
- Scanner(const StringRef Input, SourceMgr &SM);
- Scanner(MemoryBuffer *Buffer, SourceMgr &SM_);
+ Scanner(StringRef Input, SourceMgr &SM, bool ShowColors = true);
+ Scanner(MemoryBufferRef Buffer, SourceMgr &SM_, bool ShowColors = true);
/// @brief Parse the next token and return it without popping it.
Token &peekNext();
void printError(SMLoc Loc, SourceMgr::DiagKind Kind, const Twine &Message,
ArrayRef<SMRange> Ranges = None) {
- SM.PrintMessage(Loc, Kind, Message, Ranges);
+ SM.PrintMessage(Loc, Kind, Message, Ranges, /* FixIts= */ None, ShowColors);
}
void setError(const Twine &Message, StringRef::iterator Position) {
}
private:
+ void init(MemoryBufferRef Buffer);
+
StringRef currentInput() {
return StringRef(Current, End - Current);
}
/// b-break.
StringRef::iterator skip_b_break(StringRef::iterator Position);
+ /// Skip a single s-space[31] starting at Position.
+ ///
+ /// An s-space is 0x20
+ ///
+ /// @returns The code unit after the s-space, or Position if it's not a
+ /// s-space.
+ StringRef::iterator skip_s_space(StringRef::iterator Position);
+
/// @brief Skip a single s-white[33] starting at Position.
///
/// A s-white is 0x20 | 0x9
StringRef::iterator skip_while( SkipWhileFunc Func
, StringRef::iterator Position);
+ /// Skip minimal well-formed code unit subsequences until Func returns its
+ /// input.
+ void advanceWhile(SkipWhileFunc Func);
+
/// @brief Scan ns-uri-char[39]s starting at Cur.
///
/// This updates Cur and Column while scanning.
/// sequence of ns-uri-char.
StringRef scan_ns_uri_char();
- /// @brief Scan ns-plain-one-line[133] starting at \a Cur.
- StringRef scan_ns_plain_one_line();
-
/// @brief Consume a minimal well-formed code unit subsequence starting at
/// \a Cur. Return false if it is not the same Unicode scalar value as
/// \a Expected. This updates \a Column.
/// Pos is whitespace or a new line
bool isBlankOrBreak(StringRef::iterator Position);
+ /// Consume a single b-break[28] if it's present at the current position.
+ ///
+ /// Return false if the code unit at the current position isn't a line break.
+ bool consumeLineBreakIfPresent();
+
/// @brief If IsSimpleKeyAllowed, create and push_back a new SimpleKey.
void saveSimpleKeyCandidate( TokenQueueT::iterator Tok
, unsigned AtColumn
, Token::TokenKind Kind
, TokenQueueT::iterator InsertPoint);
+ /// @brief Skip a single-line comment when the comment starts at the current
+ /// position of the scanner.
+ void skipComment();
+
/// @brief Skip whitespace and comments until the start of the next token.
void scanToNextToken();
/// @brief Scan a block scalar starting with | or >.
bool scanBlockScalar(bool IsLiteral);
+ /// Scan a chomping indicator in a block scalar header.
+ char scanBlockChompingIndicator();
+
+ /// Scan an indentation indicator in a block scalar header.
+ unsigned scanBlockIndentationIndicator();
+
+ /// Scan a block scalar header.
+ ///
+ /// Return false if an error occurred.
+ bool scanBlockScalarHeader(char &ChompingIndicator, unsigned &IndentIndicator,
+ bool &IsDone);
+
+ /// Look for the indentation level of a block scalar.
+ ///
+ /// Return false if an error occurred.
+ bool findBlockScalarIndent(unsigned &BlockIndent, unsigned BlockExitIndent,
+ unsigned &LineBreaks, bool &IsDone);
+
+ /// Scan the indentation of a text line in a block scalar.
+ ///
+ /// Return false if an error occurred.
+ bool scanBlockScalarIndent(unsigned BlockIndent, unsigned BlockExitIndent,
+ bool &IsDone);
+
/// @brief Scan a tag of the form !stuff.
bool scanTag();
SourceMgr &SM;
/// @brief The original input.
- MemoryBuffer *InputBuffer;
+ MemoryBufferRef InputBuffer;
/// @brief The current position of the scanner.
StringRef::iterator Current;
/// @brief True if an error has occurred.
bool Failed;
+ /// @brief Should colors be used when printing out the diagnostic messages?
+ bool ShowColors;
+
/// @brief Queue of tokens. This is required to queue up tokens while looking
/// for the end of a simple key. And for cases where a single character
/// can produce multiple tokens (e.g. BlockEnd).
case Token::TK_Scalar:
OS << "Scalar: ";
break;
+ case Token::TK_BlockScalar:
+ OS << "Block Scalar: ";
+ break;
case Token::TK_Alias:
OS << "Alias: ";
break;
return EscapedInput;
}
-Scanner::Scanner(StringRef Input, SourceMgr &sm)
- : SM(sm)
- , Indent(-1)
- , Column(0)
- , Line(0)
- , FlowLevel(0)
- , IsStartOfStream(true)
- , IsSimpleKeyAllowed(true)
- , Failed(false) {
- InputBuffer = MemoryBuffer::getMemBuffer(Input, "YAML");
- SM.AddNewSourceBuffer(InputBuffer, SMLoc());
- Current = InputBuffer->getBufferStart();
- End = InputBuffer->getBufferEnd();
-}
-
-Scanner::Scanner(MemoryBuffer *Buffer, SourceMgr &SM_)
- : SM(SM_)
- , InputBuffer(Buffer)
- , Current(InputBuffer->getBufferStart())
- , End(InputBuffer->getBufferEnd())
- , Indent(-1)
- , Column(0)
- , Line(0)
- , FlowLevel(0)
- , IsStartOfStream(true)
- , IsSimpleKeyAllowed(true)
- , Failed(false) {
- SM.AddNewSourceBuffer(InputBuffer, SMLoc());
+Scanner::Scanner(StringRef Input, SourceMgr &sm, bool ShowColors)
+ : SM(sm), ShowColors(ShowColors) {
+ init(MemoryBufferRef(Input, "YAML"));
+}
+
+Scanner::Scanner(MemoryBufferRef Buffer, SourceMgr &SM_, bool ShowColors)
+ : SM(SM_), ShowColors(ShowColors) {
+ init(Buffer);
+}
+
+void Scanner::init(MemoryBufferRef Buffer) {
+ InputBuffer = Buffer;
+ Current = InputBuffer.getBufferStart();
+ End = InputBuffer.getBufferEnd();
+ Indent = -1;
+ Column = 0;
+ Line = 0;
+ FlowLevel = 0;
+ IsStartOfStream = true;
+ IsSimpleKeyAllowed = true;
+ Failed = false;
+ std::unique_ptr<MemoryBuffer> InputBufferOwner =
+ MemoryBuffer::getMemBuffer(Buffer);
+ SM.AddNewSourceBuffer(std::move(InputBufferOwner), SMLoc());
}
Token &Scanner::peekNext() {
return Position;
}
+StringRef::iterator Scanner::skip_s_space(StringRef::iterator Position) {
+ if (Position == End)
+ return Position;
+ if (*Position == ' ')
+ return Position + 1;
+ return Position;
+}
StringRef::iterator Scanner::skip_s_white(StringRef::iterator Position) {
if (Position == End)
return Position;
}
+void Scanner::advanceWhile(SkipWhileFunc Func) {
+ auto Final = skip_while(Func, Current);
+ Column += Final - Current;
+ Current = Final;
+}
+
static bool is_ns_hex_digit(const char C) {
return (C >= '0' && C <= '9')
|| (C >= 'a' && C <= 'z')
return StringRef(Start, Current - Start);
}
-StringRef Scanner::scan_ns_plain_one_line() {
- StringRef::iterator start = Current;
- // The first character must already be verified.
- ++Current;
- while (true) {
- if (Current == End) {
- break;
- } else if (*Current == ':') {
- // Check if the next character is a ns-char.
- if (Current + 1 == End)
- break;
- StringRef::iterator i = skip_ns_char(Current + 1);
- if (Current + 1 != i) {
- Current = i;
- Column += 2; // Consume both the ':' and ns-char.
- } else
- break;
- } else if (*Current == '#') {
- // Check if the previous character was a ns-char.
- // The & 0x80 check is to check for the trailing byte of a utf-8
- if (*(Current - 1) & 0x80 || skip_ns_char(Current - 1) == Current) {
- ++Current;
- ++Column;
- } else
- break;
- } else {
- StringRef::iterator i = skip_nb_char(Current);
- if (i == Current)
- break;
- Current = i;
- ++Column;
- }
- }
- return StringRef(start, Current - start);
-}
-
bool Scanner::consume(uint32_t Expected) {
if (Expected >= 0x80)
report_fatal_error("Not dealing with this yet");
return false;
}
+bool Scanner::consumeLineBreakIfPresent() {
+ auto Next = skip_b_break(Current);
+ if (Next == Current)
+ return false;
+ Column = 0;
+ ++Line;
+ Current = Next;
+ return true;
+}
+
void Scanner::saveSimpleKeyCandidate( TokenQueueT::iterator Tok
, unsigned AtColumn
, bool IsRequired) {
return true;
}
+void Scanner::skipComment() {
+ if (*Current != '#')
+ return;
+ while (true) {
+ // This may skip more than one byte, thus Column is only incremented
+ // for code points.
+ StringRef::iterator I = skip_nb_char(Current);
+ if (I == Current)
+ break;
+ Current = I;
+ ++Column;
+ }
+}
+
void Scanner::scanToNextToken() {
while (true) {
while (*Current == ' ' || *Current == '\t') {
skip(1);
}
- // Skip comment.
- if (*Current == '#') {
- while (true) {
- // This may skip more than one byte, thus Column is only incremented
- // for code points.
- StringRef::iterator i = skip_nb_char(Current);
- if (i == Current)
- break;
- Current = i;
- ++Column;
- }
- }
+ skipComment();
// Skip EOL.
StringRef::iterator i = skip_b_break(Current);
return true;
}
-bool Scanner::scanBlockScalar(bool IsLiteral) {
- StringRef::iterator Start = Current;
- skip(1); // Eat | or >
- while(true) {
- StringRef::iterator i = skip_nb_char(Current);
- if (i == Current) {
- if (Column == 0)
- break;
- i = skip_b_break(Current);
- if (i != Current) {
- // We got a line break.
- Column = 0;
- ++Line;
- Current = i;
- continue;
- } else {
- // There was an error, which should already have been printed out.
+char Scanner::scanBlockChompingIndicator() {
+ char Indicator = ' ';
+ if (Current != End && (*Current == '+' || *Current == '-')) {
+ Indicator = *Current;
+ skip(1);
+ }
+ return Indicator;
+}
+
+/// Get the number of line breaks after chomping.
+///
+/// Return the number of trailing line breaks to emit, depending on
+/// \p ChompingIndicator.
+static unsigned getChompedLineBreaks(char ChompingIndicator,
+ unsigned LineBreaks, StringRef Str) {
+ if (ChompingIndicator == '-') // Strip all line breaks.
+ return 0;
+ if (ChompingIndicator == '+') // Keep all line breaks.
+ return LineBreaks;
+ // Clip trailing lines.
+ return Str.empty() ? 0 : 1;
+}
+
+unsigned Scanner::scanBlockIndentationIndicator() {
+ unsigned Indent = 0;
+ if (Current != End && (*Current >= '1' && *Current <= '9')) {
+ Indent = unsigned(*Current - '0');
+ skip(1);
+ }
+ return Indent;
+}
+
+bool Scanner::scanBlockScalarHeader(char &ChompingIndicator,
+ unsigned &IndentIndicator, bool &IsDone) {
+ auto Start = Current;
+
+ ChompingIndicator = scanBlockChompingIndicator();
+ IndentIndicator = scanBlockIndentationIndicator();
+ // Check for the chomping indicator once again.
+ if (ChompingIndicator == ' ')
+ ChompingIndicator = scanBlockChompingIndicator();
+ Current = skip_while(&Scanner::skip_s_white, Current);
+ skipComment();
+
+ if (Current == End) { // EOF, we have an empty scalar.
+ Token T;
+ T.Kind = Token::TK_BlockScalar;
+ T.Range = StringRef(Start, Current - Start);
+ TokenQueue.push_back(T);
+ IsDone = true;
+ return true;
+ }
+
+ if (!consumeLineBreakIfPresent()) {
+ setError("Expected a line break after block scalar header", Current);
+ return false;
+ }
+ return true;
+}
+
+bool Scanner::findBlockScalarIndent(unsigned &BlockIndent,
+ unsigned BlockExitIndent,
+ unsigned &LineBreaks, bool &IsDone) {
+ unsigned MaxAllSpaceLineCharacters = 0;
+ StringRef::iterator LongestAllSpaceLine;
+
+ while (true) {
+ advanceWhile(&Scanner::skip_s_space);
+ if (skip_nb_char(Current) != Current) {
+ // This line isn't empty, so try and find the indentation.
+ if (Column <= BlockExitIndent) { // End of the block literal.
+ IsDone = true;
+ return true;
+ }
+ // We found the block's indentation.
+ BlockIndent = Column;
+ if (MaxAllSpaceLineCharacters > BlockIndent) {
+ setError(
+ "Leading all-spaces line must be smaller than the block indent",
+ LongestAllSpaceLine);
return false;
}
+ return true;
}
- Current = i;
+ if (skip_b_break(Current) != Current &&
+ Column > MaxAllSpaceLineCharacters) {
+ // Record the longest all-space line in case it's longer than the
+ // discovered block indent.
+ MaxAllSpaceLineCharacters = Column;
+ LongestAllSpaceLine = Current;
+ }
+
+ // Check for EOF.
+ if (Current == End) {
+ IsDone = true;
+ return true;
+ }
+
+ if (!consumeLineBreakIfPresent()) {
+ IsDone = true;
+ return true;
+ }
+ ++LineBreaks;
+ }
+ return true;
+}
+
+bool Scanner::scanBlockScalarIndent(unsigned BlockIndent,
+ unsigned BlockExitIndent, bool &IsDone) {
+ // Skip the indentation.
+ while (Column < BlockIndent) {
+ auto I = skip_s_space(Current);
+ if (I == Current)
+ break;
+ Current = I;
++Column;
}
- if (Start == Current) {
- setError("Got empty block scalar", Start);
+ if (skip_nb_char(Current) == Current)
+ return true;
+
+ if (Column <= BlockExitIndent) { // End of the block literal.
+ IsDone = true;
+ return true;
+ }
+
+ if (Column < BlockIndent) {
+ if (Current != End && *Current == '#') { // Trailing comment.
+ IsDone = true;
+ return true;
+ }
+ setError("A text line is less indented than the block scalar", Current);
return false;
}
+ return true; // A normal text line.
+}
+
+bool Scanner::scanBlockScalar(bool IsLiteral) {
+ // Eat '|' or '>'
+ assert(*Current == '|' || *Current == '>');
+ skip(1);
+
+ char ChompingIndicator;
+ unsigned BlockIndent;
+ bool IsDone = false;
+ if (!scanBlockScalarHeader(ChompingIndicator, BlockIndent, IsDone))
+ return false;
+ if (IsDone)
+ return true;
+
+ auto Start = Current;
+ unsigned BlockExitIndent = Indent < 0 ? 0 : (unsigned)Indent;
+ unsigned LineBreaks = 0;
+ if (BlockIndent == 0) {
+ if (!findBlockScalarIndent(BlockIndent, BlockExitIndent, LineBreaks,
+ IsDone))
+ return false;
+ }
+
+ // Scan the block's scalars body.
+ SmallString<256> Str;
+ while (!IsDone) {
+ if (!scanBlockScalarIndent(BlockIndent, BlockExitIndent, IsDone))
+ return false;
+ if (IsDone)
+ break;
+
+ // Parse the current line.
+ auto LineStart = Current;
+ advanceWhile(&Scanner::skip_nb_char);
+ if (LineStart != Current) {
+ Str.append(LineBreaks, '\n');
+ Str.append(StringRef(LineStart, Current - LineStart));
+ LineBreaks = 0;
+ }
+
+ // Check for EOF.
+ if (Current == End)
+ break;
+
+ if (!consumeLineBreakIfPresent())
+ break;
+ ++LineBreaks;
+ }
+
+ if (Current == End && !LineBreaks)
+ // Ensure that there is at least one line break before the end of file.
+ LineBreaks = 1;
+ Str.append(getChompedLineBreaks(ChompingIndicator, LineBreaks, Str), '\n');
+
+ // New lines may start a simple key.
+ if (!FlowLevel)
+ IsSimpleKeyAllowed = true;
Token T;
- T.Kind = Token::TK_Scalar;
+ T.Kind = Token::TK_BlockScalar;
T.Range = StringRef(Start, Current - Start);
+ T.Value = Str.str().str();
TokenQueue.push_back(T);
return true;
}
return false;
}
-Stream::Stream(StringRef Input, SourceMgr &SM)
- : scanner(new Scanner(Input, SM))
- , CurrentDoc(0) {}
+Stream::Stream(StringRef Input, SourceMgr &SM, bool ShowColors)
+ : scanner(new Scanner(Input, SM, ShowColors)), CurrentDoc() {}
-Stream::Stream(MemoryBuffer *InputBuffer, SourceMgr &SM)
- : scanner(new Scanner(InputBuffer, SM))
- , CurrentDoc(0) {}
+Stream::Stream(MemoryBufferRef InputBuffer, SourceMgr &SM, bool ShowColors)
+ : scanner(new Scanner(InputBuffer, SM, ShowColors)), CurrentDoc() {}
Stream::~Stream() {}
bool Stream::failed() { return scanner->failed(); }
void Stream::printError(Node *N, const Twine &Msg) {
- SmallVector<SMRange, 1> Ranges;
- Ranges.push_back(N->getSourceRange());
scanner->printError( N->getSourceRange().Start
, SourceMgr::DK_Error
, Msg
- , Ranges);
+ , N->getSourceRange());
}
document_iterator Stream::begin() {
i->skip();
}
-Node::Node(unsigned int Type, OwningPtr<Document> &D, StringRef A, StringRef T)
- : Doc(D)
- , TypeID(Type)
- , Anchor(A)
- , Tag(T) {
+Node::Node(unsigned int Type, std::unique_ptr<Document> &D, StringRef A,
+ StringRef T)
+ : Doc(D), TypeID(Type), Anchor(A), Tag(T) {
SMLoc Start = SMLoc::getFromPointer(peekNext().Range.begin());
SourceRange = SMRange(Start, Start);
}
if (Raw.find_last_of('!') == 0) {
Ret = Doc->getTagMap().find("!")->second;
Ret += Raw.substr(1);
- return llvm_move(Ret);
+ return Ret;
} else if (Raw.startswith("!!")) {
Ret = Doc->getTagMap().find("!!")->second;
Ret += Raw.substr(2);
- return llvm_move(Ret);
+ return Ret;
} else {
StringRef TagHandle = Raw.substr(0, Raw.find_last_of('!') + 1);
std::map<StringRef, StringRef>::const_iterator It =
setError(Twine("Unknown tag handle ") + TagHandle, T);
}
Ret += Raw.substr(Raw.find_last_of('!') + 1);
- return llvm_move(Ret);
+ return Ret;
}
}
case NK_Null:
return "tag:yaml.org,2002:null";
case NK_Scalar:
+ case NK_BlockScalar:
// TODO: Tag resolution.
return "tag:yaml.org,2002:str";
case NK_Mapping:
void MappingNode::increment() {
if (failed()) {
IsAtEnd = true;
- CurrentEntry = 0;
+ CurrentEntry = nullptr;
return;
}
if (CurrentEntry) {
CurrentEntry->skip();
if (Type == MT_Inline) {
IsAtEnd = true;
- CurrentEntry = 0;
+ CurrentEntry = nullptr;
return;
}
}
case Token::TK_BlockEnd:
getNext();
IsAtEnd = true;
- CurrentEntry = 0;
+ CurrentEntry = nullptr;
break;
default:
setError("Unexpected token. Expected Key or Block End", T);
case Token::TK_Error:
IsAtEnd = true;
- CurrentEntry = 0;
+ CurrentEntry = nullptr;
}
} else {
switch (T.Kind) {
case Token::TK_Error:
// Set this to end iterator.
IsAtEnd = true;
- CurrentEntry = 0;
+ CurrentEntry = nullptr;
break;
default:
setError( "Unexpected token. Expected Key, Flow Entry, or Flow "
"Mapping End."
, T);
IsAtEnd = true;
- CurrentEntry = 0;
+ CurrentEntry = nullptr;
}
}
}
void SequenceNode::increment() {
if (failed()) {
IsAtEnd = true;
- CurrentEntry = 0;
+ CurrentEntry = nullptr;
return;
}
if (CurrentEntry)
case Token::TK_BlockEntry:
getNext();
CurrentEntry = parseBlockNode();
- if (CurrentEntry == 0) { // An error occurred.
+ if (!CurrentEntry) { // An error occurred.
IsAtEnd = true;
- CurrentEntry = 0;
+ CurrentEntry = nullptr;
}
break;
case Token::TK_BlockEnd:
getNext();
IsAtEnd = true;
- CurrentEntry = 0;
+ CurrentEntry = nullptr;
break;
default:
setError( "Unexpected token. Expected Block Entry or Block End."
, T);
case Token::TK_Error:
IsAtEnd = true;
- CurrentEntry = 0;
+ CurrentEntry = nullptr;
}
} else if (SeqType == ST_Indentless) {
switch (T.Kind) {
case Token::TK_BlockEntry:
getNext();
CurrentEntry = parseBlockNode();
- if (CurrentEntry == 0) { // An error occurred.
+ if (!CurrentEntry) { // An error occurred.
IsAtEnd = true;
- CurrentEntry = 0;
+ CurrentEntry = nullptr;
}
break;
default:
case Token::TK_Error:
IsAtEnd = true;
- CurrentEntry = 0;
+ CurrentEntry = nullptr;
}
} else if (SeqType == ST_Flow) {
switch (T.Kind) {
case Token::TK_Error:
// Set this to end iterator.
IsAtEnd = true;
- CurrentEntry = 0;
+ CurrentEntry = nullptr;
break;
case Token::TK_StreamEnd:
case Token::TK_DocumentEnd:
setError("Could not find closing ]!", T);
// Set this to end iterator.
IsAtEnd = true;
- CurrentEntry = 0;
+ CurrentEntry = nullptr;
break;
default:
if (!WasPreviousTokenFlowEntry) {
setError("Expected , between entries!", T);
IsAtEnd = true;
- CurrentEntry = 0;
+ CurrentEntry = nullptr;
break;
}
// Otherwise it must be a flow entry.
}
}
-Document::Document(Stream &S) : stream(S), Root(0) {
+Document::Document(Stream &S) : stream(S), Root(nullptr) {
// Tag maps starts with two default mappings.
TagMap["!"] = "!";
TagMap["!!"] = "tag:yaml.org,2002:";
case Token::TK_Anchor:
if (AnchorInfo.Kind == Token::TK_Anchor) {
setError("Already encountered an anchor for this node!", T);
- return 0;
+ return nullptr;
}
AnchorInfo = getNext(); // Consume TK_Anchor.
T = peekNext();
case Token::TK_Tag:
if (TagInfo.Kind == Token::TK_Tag) {
setError("Already encountered a tag for this node!", T);
- return 0;
+ return nullptr;
}
TagInfo = getNext(); // Consume TK_Tag.
T = peekNext();
, AnchorInfo.Range.substr(1)
, TagInfo.Range
, T.Range);
+ case Token::TK_BlockScalar: {
+ getNext();
+ StringRef NullTerminatedStr(T.Value.c_str(), T.Value.length() + 1);
+ StringRef StrCopy = NullTerminatedStr.copy(NodeAllocator).drop_back();
+ return new (NodeAllocator)
+ BlockScalarNode(stream.CurrentDoc, AnchorInfo.Range.substr(1),
+ TagInfo.Range, StrCopy, T.Range);
+ }
case Token::TK_Key:
// Don't eat the TK_Key, KeyValueNode expects it.
return new (NodeAllocator)
// !!null null.
return new (NodeAllocator) NullNode(stream.CurrentDoc);
case Token::TK_Error:
- return 0;
+ return nullptr;
}
llvm_unreachable("Control flow shouldn't reach here.");
- return 0;
+ return nullptr;
}
bool Document::parseDirectives() {