Token ScanBlockScalar(bool isLiteral)
{
var value = new StringBuilder();
var leadingBreak = new StringBuilder();
var trailingBreaks = new StringBuilder();
int chomping = 0;
int increment = 0;
int currentIndent = 0;
bool leadingBlank = false;
// Eat the indicator '|' or '>'.
var start = cursor.Mark();
Skip();
// Check for a chomping indicator.
if (analyzer.Check("+-"))
{
// Set the chomping method and eat the indicator.
chomping = analyzer.Check('+') ? +1 : -1;
Skip();
// Check for an indentation indicator.
if (analyzer.IsDigit())
{
// Check that the intendation is greater than 0.
if (analyzer.Check('0'))
{
throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a block scalar, find an intendation indicator equal to 0.");
}
// Get the intendation level and eat the indicator.
increment = analyzer.AsDigit();
Skip();
}
}
// Do the same as above, but in the opposite order.
else if (analyzer.IsDigit())
{
if (analyzer.Check('0'))
{
throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a block scalar, find an intendation indicator equal to 0.");
}
increment = analyzer.AsDigit();
Skip();
if (analyzer.Check("+-"))
{
chomping = analyzer.Check('+') ? +1 : -1;
Skip();
}
}
// Eat whitespaces and comments to the end of the line.
while (analyzer.IsWhite())
{
Skip();
}
ProcessComment();
// Check if we are at the end of the line.
if (!analyzer.IsBreakOrZero())
{
throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a block scalar, did not find expected comment or line break.");
}
// Eat a line break.
if (analyzer.IsBreak())
{
SkipLine();
}
var end = cursor.Mark();
// Set the intendation level if it was specified.
if (increment != 0)
{
currentIndent = indent >= 0 ? indent + increment : increment;
}
// Scan the leading line breaks and determine the indentation level if needed.
currentIndent = ScanBlockScalarBreaks(currentIndent, trailingBreaks, start, ref end);
// Scan the block scalar content.
while (cursor.LineOffset == currentIndent && !analyzer.IsZero())
{
// We are at the beginning of a non-empty line.
// Is it a trailing whitespace?
var trailingBlank = analyzer.IsWhite();
// Check if we need to fold the leading line break.
if (!isLiteral && StartsWith(leadingBreak, '\n') && !leadingBlank && !trailingBlank)
{
// Do we need to join the lines by space?
if (trailingBreaks.Length == 0)
{
value.Append(' ');
}
leadingBreak.Length = 0;
}
else
{
value.Append(leadingBreak.ToString());
leadingBreak.Length = 0;
}
// Append the remaining line breaks.
value.Append(trailingBreaks.ToString());
trailingBreaks.Length = 0;
// Is it a leading whitespace?
leadingBlank = analyzer.IsWhite();
// Consume the current line.
while (!analyzer.IsBreakOrZero())
{
value.Append(ReadCurrentCharacter());
}
// Consume the line break.
var lineBreak = ReadLine();
if (lineBreak != '\0')
{
leadingBreak.Append(lineBreak);
}
// Eat the following intendation spaces and line breaks.
currentIndent = ScanBlockScalarBreaks(currentIndent, trailingBreaks, start, ref end);
}
// Chomp the tail.
if (chomping != -1)
{
value.Append(leadingBreak);
}
if (chomping == 1)
{
value.Append(trailingBreaks);
}
// Create a token.
ScalarStyle style = isLiteral ? ScalarStyle.Literal : ScalarStyle.Folded;
return new Scalar(value.ToString(), style, start, end);
}