You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
|
|
#include "scanscalar.h"
#include <algorithm>
#include "exp.h"
#include "regeximpl.h"
#include "stream.h"
#include "yaml-cpp/exceptions.h" // IWYU pragma: keep
namespace YAML { // ScanScalar
// . This is where the scalar magic happens.
//
// . We do the scanning in three phases:
// 1. Scan until newline
// 2. Eat newline
// 3. Scan leading blanks.
//
// . Depending on the parameters given, we store or stop
// and different places in the above flow.
std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) { bool foundNonEmptyLine = false; bool pastOpeningBreak = (params.fold == FOLD_FLOW); bool emptyLine = false, moreIndented = false; int foldedNewlineCount = 0; bool foldedNewlineStartedMoreIndented = false; std::size_t lastEscapedChar = std::string::npos; std::string scalar; params.leadingSpaces = false;
if (!params.end) { params.end = &Exp::Empty(); }
while (INPUT) { // ********************************
// Phase #1: scan until line ending
std::size_t lastNonWhitespaceChar = scalar.size(); bool escapedNewline = false; while (!params.end->Matches(INPUT) && !Exp::Break().Matches(INPUT)) { if (!INPUT) { break; }
// document indicator?
if (INPUT.column() == 0 && Exp::DocIndicator().Matches(INPUT)) { if (params.onDocIndicator == BREAK) { break; } if (params.onDocIndicator == THROW) { throw ParserException(INPUT.mark(), ErrorMsg::DOC_IN_SCALAR); } }
foundNonEmptyLine = true; pastOpeningBreak = true;
// escaped newline? (only if we're escaping on slash)
if (params.escape == '\\' && Exp::EscBreak().Matches(INPUT)) { // eat escape character and get out (but preserve trailing whitespace!)
INPUT.get(); lastNonWhitespaceChar = scalar.size(); lastEscapedChar = scalar.size(); escapedNewline = true; break; }
// escape this?
if (INPUT.peek() == params.escape) { scalar += Exp::Escape(INPUT); lastNonWhitespaceChar = scalar.size(); lastEscapedChar = scalar.size(); continue; }
// otherwise, just add the damn character
char ch = INPUT.get(); scalar += ch; if (ch != ' ' && ch != '\t') { lastNonWhitespaceChar = scalar.size(); } }
// eof? if we're looking to eat something, then we throw
if (!INPUT) { if (params.eatEnd) { throw ParserException(INPUT.mark(), ErrorMsg::EOF_IN_SCALAR); } break; }
// doc indicator?
if (params.onDocIndicator == BREAK && INPUT.column() == 0 && Exp::DocIndicator().Matches(INPUT)) { break; }
// are we done via character match?
int n = params.end->Match(INPUT); if (n >= 0) { if (params.eatEnd) { INPUT.eat(n); } break; }
// do we remove trailing whitespace?
if (params.fold == FOLD_FLOW) scalar.erase(lastNonWhitespaceChar);
// ********************************
// Phase #2: eat line ending
n = Exp::Break().Match(INPUT); INPUT.eat(n);
// ********************************
// Phase #3: scan initial spaces
// first the required indentation
while (INPUT.peek() == ' ' && (INPUT.column() < params.indent || (params.detectIndent && !foundNonEmptyLine)) && !params.end->Matches(INPUT)) { INPUT.eat(1); }
// update indent if we're auto-detecting
if (params.detectIndent && !foundNonEmptyLine) { params.indent = std::max(params.indent, INPUT.column()); }
// and then the rest of the whitespace
while (Exp::Blank().Matches(INPUT)) { // we check for tabs that masquerade as indentation
if (INPUT.peek() == '\t' && INPUT.column() < params.indent && params.onTabInIndentation == THROW) { throw ParserException(INPUT.mark(), ErrorMsg::TAB_IN_INDENTATION); }
if (!params.eatLeadingWhitespace) { break; }
if (params.end->Matches(INPUT)) { break; }
INPUT.eat(1); }
// was this an empty line?
bool nextEmptyLine = Exp::Break().Matches(INPUT); bool nextMoreIndented = Exp::Blank().Matches(INPUT); if (params.fold == FOLD_BLOCK && foldedNewlineCount == 0 && nextEmptyLine) foldedNewlineStartedMoreIndented = moreIndented;
// for block scalars, we always start with a newline, so we should ignore it
// (not fold or keep)
if (pastOpeningBreak) { switch (params.fold) { case DONT_FOLD: scalar += "\n"; break; case FOLD_BLOCK: if (!emptyLine && !nextEmptyLine && !moreIndented && !nextMoreIndented && INPUT.column() >= params.indent) { scalar += " "; } else if (nextEmptyLine) { foldedNewlineCount++; } else { scalar += "\n"; }
if (!nextEmptyLine && foldedNewlineCount > 0) { scalar += std::string(foldedNewlineCount - 1, '\n'); if (foldedNewlineStartedMoreIndented || nextMoreIndented | !foundNonEmptyLine) { scalar += "\n"; } foldedNewlineCount = 0; } break; case FOLD_FLOW: if (nextEmptyLine) { scalar += "\n"; } else if (!emptyLine && !escapedNewline) { scalar += " "; } break; } }
emptyLine = nextEmptyLine; moreIndented = nextMoreIndented; pastOpeningBreak = true;
// are we done via indentation?
if (!emptyLine && INPUT.column() < params.indent) { params.leadingSpaces = true; break; } }
// post-processing
if (params.trimTrailingSpaces) { std::size_t pos = scalar.find_last_not_of(" \t"); if (lastEscapedChar != std::string::npos) { if (pos < lastEscapedChar || pos == std::string::npos) { pos = lastEscapedChar; } } if (pos < scalar.size()) { scalar.erase(pos + 1); } }
switch (params.chomp) { case CLIP: { std::size_t pos = scalar.find_last_not_of('\n'); if (lastEscapedChar != std::string::npos) { if (pos < lastEscapedChar || pos == std::string::npos) { pos = lastEscapedChar; } } if (pos == std::string::npos) { scalar.erase(); } else if (pos + 1 < scalar.size()) { scalar.erase(pos + 2); } } break; case STRIP: { std::size_t pos = scalar.find_last_not_of('\n'); if (lastEscapedChar != std::string::npos) { if (pos < lastEscapedChar || pos == std::string::npos) { pos = lastEscapedChar; } } if (pos == std::string::npos) { scalar.erase(); } else if (pos < scalar.size()) { scalar.erase(pos + 1); } } break; default: break; }
return scalar; } } // namespace YAML
|