private Token ScanFlowScalar(bool isSingleQuoted)
{
// Eat the left quote.
var start = cursor.Mark();
Skip();
// Consume the content of the quoted scalar.
var value = new StringBuilder();
var whitespaces = new StringBuilder();
var leadingBreak = new StringBuilder();
var trailingBreaks = new StringBuilder();
for (;;)
{
// Check that there are no document indicators at the beginning of the line.
if (IsDocumentIndicator())
{
throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a quoted scalar, find unexpected document indicator.");
}
// Check for EOF.
if (analyzer.IsZero())
{
throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a quoted scalar, find unexpected end of stream.");
}
// Consume non-blank characters.
bool hasLeadingBlanks = false;
while (!analyzer.IsWhiteBreakOrZero())
{
// Check for an escaped single quote.
if (isSingleQuoted && analyzer.Check('\'', 0) && analyzer.Check('\'', 1))
{
value.Append('\'');
Skip();
Skip();
}
// Check for the right quote.
else if (analyzer.Check(isSingleQuoted ? '\'' : '"'))
{
break;
}
// Check for an escaped line break.
else if (!isSingleQuoted && analyzer.Check('\\') && analyzer.IsBreak(1))
{
Skip();
SkipLine();
hasLeadingBlanks = true;
break;
}
// Check for an escape sequence.
else if (!isSingleQuoted && analyzer.Check('\\'))
{
int codeLength = 0;
// Check the escape character.
var escapeCharacter = analyzer.Peek(1);
switch (escapeCharacter)
{
case 'x':
codeLength = 2;
break;
case 'u':
codeLength = 4;
break;
case 'U':
codeLength = 8;
break;
default:
char unescapedCharacter;
if (simpleEscapeCodes.TryGetValue(escapeCharacter, out unescapedCharacter))
{
value.Append(unescapedCharacter);
}
else
{
throw new SyntaxErrorException(start, cursor.Mark(), "While parsing a quoted scalar, find unknown escape character.");
}
break;
}
Skip();
Skip();
// Consume an arbitrary escape code.
if (codeLength > 0)
{
int character = 0;
// Scan the character value.
for (int k = 0; k < codeLength; ++k)
{
if (!analyzer.IsHex(k))
{
throw new SyntaxErrorException(start, cursor.Mark(), "While parsing a quoted scalar, did not find expected hexdecimal number.");
}
character = ((character << 4) + analyzer.AsHex(k));
}
// Check the value and write the character.
if ((character >= 0xD800 && character <= 0xDFFF) || character > 0x10FFFF)
{
throw new SyntaxErrorException(start, cursor.Mark(), "While parsing a quoted scalar, find invalid Unicode character escape code.");
}
value.Append(char.ConvertFromUtf32(character));
// Advance the pointer.
for (int k = 0; k < codeLength; ++k)
{
Skip();
}
}
}
else
{
// It is a non-escaped non-blank character.
value.Append(ReadCurrentCharacter());
}
}
// Check if we are at the end of the scalar.
if (analyzer.Check(isSingleQuoted ? '\'' : '"'))
break;
// Consume blank characters.
while (analyzer.IsWhite() || analyzer.IsBreak())
{
if (analyzer.IsWhite())
{
// Consume a space or a tab character.
if (!hasLeadingBlanks)
{
whitespaces.Append(ReadCurrentCharacter());
}
else
{
Skip();
}
}
else
{
// Check if it is a first line break.
if (!hasLeadingBlanks)
{
whitespaces.Length = 0;
leadingBreak.Append(ReadLine());
hasLeadingBlanks = true;
}
else
{
trailingBreaks.Append(ReadLine());
}
}
}
// Join the whitespaces or fold line breaks.
if (hasLeadingBlanks)
{
// Do we need to fold line breaks?
if (StartsWith(leadingBreak, '\n'))
{
if (trailingBreaks.Length == 0)
{
value.Append(' ');
}
else
{
value.Append(trailingBreaks.ToString());
}
}
else
{
value.Append(leadingBreak.ToString());
value.Append(trailingBreaks.ToString());
}
leadingBreak.Length = 0;
trailingBreaks.Length = 0;
}
else
{
value.Append(whitespaces.ToString());
whitespaces.Length = 0;
}
}
// Eat the right quote.
Skip();
return new Scalar(value.ToString(), isSingleQuoted ? ScalarStyle.SingleQuoted : ScalarStyle.DoubleQuoted, start, cursor.Mark());
}