diff --git a/llvm/include/llvm/Support/YAMLParser.h b/llvm/include/llvm/Support/YAMLParser.h --- a/llvm/include/llvm/Support/YAMLParser.h +++ b/llvm/include/llvm/Support/YAMLParser.h @@ -11,7 +11,6 @@ // See http://www.yaml.org/spec/1.2/spec.html for the full standard. // // This currently does not implement the following: -// * Multi-line literal folding. // * Tag resolution. // * UTF-16. // * BOMs anywhere other than the first Unicode scalar value in the file. diff --git a/llvm/lib/Support/YAMLParser.cpp b/llvm/lib/Support/YAMLParser.cpp --- a/llvm/lib/Support/YAMLParser.cpp +++ b/llvm/lib/Support/YAMLParser.cpp @@ -393,6 +393,9 @@ /// Pos is whitespace or a new line bool isBlankOrBreak(StringRef::iterator Position); + /// Return true if the line is a line break, false otherwise. + bool isLineEmpty(StringRef Line); + /// 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. @@ -471,6 +474,18 @@ /// Scan a block scalar starting with | or >. bool scanBlockScalar(bool IsLiteral); + /// Scan a block scalar style indicator and header. + /// + /// Note: This is distinct from scanBlockScalarHeader to mirror the fact that + /// YAML does not consider the style indicator to be a part of the header. + /// + /// Return false if an error occurred. + bool scanBlockScalarIndicators(char &StyleIndicator, char &ChompingIndicator, + unsigned &IndentIndicator, bool &IsDone); + + /// Scan a style indicator in a block scalar header. + char scanBlockStyleIndicator(); + /// Scan a chomping indicator in a block scalar header. char scanBlockChompingIndicator(); @@ -1035,6 +1050,13 @@ *Position == '\n'; } +bool Scanner::isLineEmpty(StringRef Line) { + for (const auto *Position = Line.begin(); Position != Line.end(); ++Position) + if (!isBlankOrBreak(Position)) + return false; + return true; +} + bool Scanner::consumeLineBreakIfPresent() { auto Next = skip_b_break(Current); if (Next == Current) @@ -1517,6 +1539,25 @@ return true; } +bool Scanner::scanBlockScalarIndicators(char &StyleIndicator, + char &ChompingIndicator, + unsigned &IndentIndicator, + bool &IsDone) { + StyleIndicator = scanBlockStyleIndicator(); + if (!scanBlockScalarHeader(ChompingIndicator, IndentIndicator, IsDone)) + return false; + return true; +} + +char Scanner::scanBlockStyleIndicator() { + char Indicator = ' '; + if (Current != End && (*Current == '>' || *Current == '|')) { + Indicator = *Current; + skip(1); + } + return Indicator; +} + char Scanner::scanBlockChompingIndicator() { char Indicator = ' '; if (Current != End && (*Current == '+' || *Current == '-')) { @@ -1655,19 +1696,19 @@ } bool Scanner::scanBlockScalar(bool IsLiteral) { - // Eat '|' or '>' assert(*Current == '|' || *Current == '>'); - skip(1); - + char StyleIndicator; char ChompingIndicator; unsigned BlockIndent; bool IsDone = false; - if (!scanBlockScalarHeader(ChompingIndicator, BlockIndent, IsDone)) + if (!scanBlockScalarIndicators(StyleIndicator, ChompingIndicator, BlockIndent, + IsDone)) return false; if (IsDone) return true; + bool IsFolded = StyleIndicator == '>'; - auto Start = Current; + const auto *Start = Current; unsigned BlockExitIndent = Indent < 0 ? 0 : (unsigned)Indent; unsigned LineBreaks = 0; if (BlockIndent == 0) { @@ -1688,11 +1729,22 @@ auto LineStart = Current; advanceWhile(&Scanner::skip_nb_char); if (LineStart != Current) { - Str.append(LineBreaks, '\n'); + if (IsFolded && Str.empty()) { + Str.append(LineBreaks, '\n'); + } else if (Str.back() != '\n') { + Str.append(LineBreaks, IsFolded ? ' ' : '\n'); + } + Str.append(StringRef(LineStart, Current - LineStart)); LineBreaks = 0; } + if (IsFolded && !Str.empty()) { + auto Line = StringRef(LineStart, Current - LineStart + 1); + // Handling empty lines in a folded string literal. + Str.append(isLineEmpty(Line), '\n'); + } + // Check for EOF. if (Current == End) break; diff --git a/llvm/test/YAMLParser/spec-09-24.test b/llvm/test/YAMLParser/spec-09-24.test --- a/llvm/test/YAMLParser/spec-09-24.test +++ b/llvm/test/YAMLParser/spec-09-24.test @@ -1,13 +1,52 @@ # RUN: yaml-bench -canonical %s | FileCheck %s # CHECK: ? !!str "strip" -# CHECK: : !!str "" +# CHECK: : !!str "Hello\n\nworld on multiple \n\nlines\nspaces stripped" # CHECK: ? !!str "clip" -# CHECK: : !!str "" +# CHECK: : !!str "\nHello world on multiple\nlines only one space at the \n\nend\n" # CHECK: ? !!str "keep" -# CHECK: : !!str "\n" +# CHECK: : !!str "\nHello\nworld\non\nmultiple\n\n\nlines\nwith space at the end of each\nline\n\n" +# CHECK: ? !!str "example" +# CHECK: : !!str "Several lines of text,\nwith some \"quotes\" of various 'types',\nand also a blank line:\n\nplus another line at the end." strip: >- + Hello + + world + on + multiple + + + lines + + spaces stripped clip: > + Hello + world + on + multiple + + lines + only one space at the + + + end keep: |+ + + Hello + world + on + multiple + + + lines + with space at the end of each + line + +example: |- + Several lines of text, + with some "quotes" of various 'types', + and also a blank line: + + plus another line at the end.