Merge pull request #241 from Herringway/exception-fixes

Some Exception-related fixes
merged-on-behalf-of: Basile-z <Basile-z@users.noreply.github.com>
This commit is contained in:
The Dlang Bot 2019-03-30 21:32:04 +01:00 committed by GitHub
commit a538662c7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 105 additions and 296 deletions

View file

@ -38,7 +38,7 @@ class ConstructorException : YAMLException
/// Params: msg = Error message. /// Params: msg = Error message.
/// start = Start position of the error context. /// start = Start position of the error context.
/// end = End position of the error context. /// end = End position of the error context.
this(string msg, Mark start, Mark end, string file = __FILE__, int line = __LINE__) this(string msg, Mark start, Mark end, string file = __FILE__, size_t line = __LINE__)
@safe pure nothrow @safe pure nothrow
{ {
super(msg ~ "\nstart: " ~ start.toString() ~ "\nend: " ~ end.toString(), super(msg ~ "\nstart: " ~ start.toString() ~ "\nend: " ~ end.toString(),

View file

@ -33,7 +33,7 @@ class NodeException : YAMLException
// //
// Params: msg = Error message. // Params: msg = Error message.
// start = Start position of the node. // start = Start position of the node.
this(string msg, Mark start, string file = __FILE__, int line = __LINE__) this(string msg, Mark start, string file = __FILE__, size_t line = __LINE__)
@safe @safe
{ {
super(msg ~ "\nNode at: " ~ start.toString(), file, line); super(msg ~ "\nNode at: " ~ start.toString(), file, line);

View file

@ -34,7 +34,7 @@ package:
///Exception thrown at Reader errors. ///Exception thrown at Reader errors.
class ReaderException : YAMLException class ReaderException : YAMLException
{ {
this(string msg, string file = __FILE__, int line = __LINE__) this(string msg, string file = __FILE__, size_t line = __LINE__)
@safe pure nothrow @safe pure nothrow
{ {
super("Reader error: " ~ msg, file, line); super("Reader error: " ~ msg, file, line);

View file

@ -150,25 +150,6 @@ struct Scanner
/// Possible simple keys indexed by flow levels. /// Possible simple keys indexed by flow levels.
SimpleKey[] possibleSimpleKeys_; SimpleKey[] possibleSimpleKeys_;
/// Set on error by nothrow/@nogc inner functions along with errorData_.
///
/// Non-nothrow/GC-using caller functions can then throw an exception using
/// data stored in errorData_.
bool error_;
/// Data for the exception to throw if error_ is true.
MarkedYAMLExceptionData errorData_;
/// Error messages can be built in this buffer without using the GC.
///
/// ScannerException (MarkedYAMLException) copies string data passed to its
/// constructor so it's safe to use slices of this buffer as parameters for
/// exceptions that may outlive the Scanner. The GC allocation when creating the
/// error message is removed, but the allocation when creating an exception is
/// not.
char[256] msgBuffer_;
public: public:
/// Construct a Scanner using specified Reader. /// Construct a Scanner using specified Reader.
this(Reader reader) @safe nothrow this(Reader reader) @safe nothrow
@ -203,45 +184,11 @@ struct Scanner
} }
private: private:
/// Build an error message in msgBuffer_ and return it as a string.
string buildMsg(S ...)(S args)
{
try {
return text(args);
}
catch (Exception)
{
return "";
}
}
/// Most scanning error messages have the same format; so build them with this /// Most scanning error messages have the same format; so build them with this
/// function. /// function.
string expected(T)(string expected, T found) string expected(T)(string expected, T found)
{ {
return buildMsg("expected ", expected, ", but found ", found); return text("expected ", expected, ", but found ", found);
}
/// If error_ is true, throws a ScannerException constructed from errorData_ and
/// sets error_ to false.
void throwIfError() @safe pure
{
if(!error_) { return; }
error_ = false;
throw new ScannerException(errorData_);
}
/// Called by internal nothrow/@nogc methods to set an error to be thrown by
/// their callers.
///
/// See_Also: dyaml.exception.MarkedYamlException
void error(string context, const Mark contextMark, string problem,
const Mark problemMark) @safe pure nothrow @nogc
{
assert(error_ == false,
"Setting an error when there already is a not yet thrown error");
error_ = true;
errorData_ = MarkedYAMLExceptionData(context, contextMark, problem, problemMark);
} }
/// Determine whether or not we need to fetch more tokens before peeking/getting a token. /// Determine whether or not we need to fetch more tokens before peeking/getting a token.
@ -462,7 +409,6 @@ struct Scanner
allowSimpleKey_ = false; allowSimpleKey_ = false;
auto directive = scanDirective(); auto directive = scanDirective();
throwIfError();
tokens_.push(directive); tokens_.push(directive);
} }
@ -646,7 +592,6 @@ struct Scanner
allowSimpleKey_ = false; allowSimpleKey_ = false;
auto anchor = scanAnchor(id); auto anchor = scanAnchor(id);
throwIfError();
tokens_.push(anchor); tokens_.push(anchor);
} }
@ -663,7 +608,6 @@ struct Scanner
allowSimpleKey_ = false; allowSimpleKey_ = false;
tokens_.push(scanTag()); tokens_.push(scanTag());
throwIfError();
} }
/// Add block SCALAR token. /// Add block SCALAR token.
@ -676,7 +620,6 @@ struct Scanner
allowSimpleKey_ = true; allowSimpleKey_ = true;
auto blockScalar = scanBlockScalar(style); auto blockScalar = scanBlockScalar(style);
throwIfError();
tokens_.push(blockScalar); tokens_.push(blockScalar);
} }
@ -694,7 +637,6 @@ struct Scanner
// Scan and add SCALAR. // Scan and add SCALAR.
auto scalar = scanFlowScalar(quotes); auto scalar = scanFlowScalar(quotes);
throwIfError();
tokens_.push(scalar); tokens_.push(scalar);
} }
@ -711,7 +653,6 @@ struct Scanner
// change this flag if the scan is finished at the beginning of the line. // change this flag if the scan is finished at the beginning of the line.
allowSimpleKey_ = false; allowSimpleKey_ = false;
auto plain = scanPlain(); auto plain = scanPlain();
throwIfError();
// Scan and add SCALAR. May change allowSimpleKey_ // Scan and add SCALAR. May change allowSimpleKey_
tokens_.push(plain); tokens_.push(plain);
@ -802,21 +743,14 @@ struct Scanner
/// ///
/// Assumes that the caller is building a slice in Reader, and puts the scanned /// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice. /// characters into that slice.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
void scanAlphaNumericToSlice(string name)(const Mark startMark) void scanAlphaNumericToSlice(string name)(const Mark startMark)
{ {
size_t length; size_t length;
dchar c = reader_.peek(); dchar c = reader_.peek();
while(c.isAlphaNum || c.among!('-', '_')) { c = reader_.peek(++length); } while(c.isAlphaNum || c.among!('-', '_')) { c = reader_.peek(++length); }
if(length == 0) enforce(length > 0, new ScannerException("While scanning " ~ name,
{ startMark, expected("alphanumeric, '-' or '_'", c), reader_.mark));
enum contextMsg = "While scanning " ~ name;
error(contextMsg, startMark, expected("alphanumeric, '-' or '_'", c),
reader_.mark);
return;
}
reader_.sliceBuilder.write(reader_.get(length)); reader_.sliceBuilder.write(reader_.get(length));
} }
@ -890,7 +824,6 @@ struct Scanner
// Scan directive name // Scan directive name
reader_.sliceBuilder.begin(); reader_.sliceBuilder.begin();
scanDirectiveNameToSlice(startMark); scanDirectiveNameToSlice(startMark);
if(error_) { return Token.init; }
const name = reader_.sliceBuilder.finish(); const name = reader_.sliceBuilder.finish();
reader_.sliceBuilder.begin(); reader_.sliceBuilder.begin();
@ -899,7 +832,6 @@ struct Scanner
uint tagHandleEnd = uint.max; uint tagHandleEnd = uint.max;
if(name == "YAML") { scanYAMLDirectiveValueToSlice(startMark); } if(name == "YAML") { scanYAMLDirectiveValueToSlice(startMark); }
else if(name == "TAG") { tagHandleEnd = scanTagDirectiveValueToSlice(startMark); } else if(name == "TAG") { tagHandleEnd = scanTagDirectiveValueToSlice(startMark); }
if(error_) { return Token.init; }
char[] value = reader_.sliceBuilder.finish(); char[] value = reader_.sliceBuilder.finish();
Mark endMark = reader_.mark; Mark endMark = reader_.mark;
@ -914,7 +846,6 @@ struct Scanner
} }
scanDirectiveIgnoredLine(startMark); scanDirectiveIgnoredLine(startMark);
if(error_) { return Token.init; }
return directiveToken(startMark, endMark, value, directive, tagHandleEnd); return directiveToken(startMark, endMark, value, directive, tagHandleEnd);
} }
@ -923,66 +854,49 @@ struct Scanner
/// ///
/// Assumes that the caller is building a slice in Reader, and puts the scanned /// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice. /// characters into that slice.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
void scanDirectiveNameToSlice(const Mark startMark) @safe void scanDirectiveNameToSlice(const Mark startMark) @safe
{ {
// Scan directive name. // Scan directive name.
scanAlphaNumericToSlice!"a directive"(startMark); scanAlphaNumericToSlice!"a directive"(startMark);
if(error_) { return; }
if(reader_.peek().among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029')) { return; } enforce(reader_.peek().among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029'),
error("While scanning a directive", startMark, new ScannerException("While scanning a directive", startMark,
expected("alphanumeric, '-' or '_'", reader_.peek()), reader_.mark); expected("alphanumeric, '-' or '_'", reader_.peek()), reader_.mark));
} }
/// Scan value of a YAML directive token. Returns major, minor version separated by '.'. /// Scan value of a YAML directive token. Returns major, minor version separated by '.'.
/// ///
/// Assumes that the caller is building a slice in Reader, and puts the scanned /// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice. /// characters into that slice.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
void scanYAMLDirectiveValueToSlice(const Mark startMark) @safe void scanYAMLDirectiveValueToSlice(const Mark startMark) @safe
{ {
findNextNonSpace(); findNextNonSpace();
scanYAMLDirectiveNumberToSlice(startMark); scanYAMLDirectiveNumberToSlice(startMark);
if(error_) { return; }
if(reader_.peekByte() != '.') enforce(reader_.peekByte() == '.',
{ new ScannerException("While scanning a directive", startMark,
error("While scanning a directive", startMark, expected("digit or '.'", reader_.peek()), reader_.mark));
expected("digit or '.'", reader_.peek()), reader_.mark);
return;
}
// Skip the '.'. // Skip the '.'.
reader_.forward(); reader_.forward();
reader_.sliceBuilder.write('.'); reader_.sliceBuilder.write('.');
scanYAMLDirectiveNumberToSlice(startMark); scanYAMLDirectiveNumberToSlice(startMark);
if(error_) { return; }
if(!reader_.peek().among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029')) enforce(reader_.peek().among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029'),
{ new ScannerException("While scanning a directive", startMark,
error("While scanning a directive", startMark, expected("digit or '.'", reader_.peek()), reader_.mark));
expected("digit or '.'", reader_.peek()), reader_.mark);
}
} }
/// Scan a number from a YAML directive. /// Scan a number from a YAML directive.
/// ///
/// Assumes that the caller is building a slice in Reader, and puts the scanned /// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice. /// characters into that slice.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
void scanYAMLDirectiveNumberToSlice(const Mark startMark) @safe void scanYAMLDirectiveNumberToSlice(const Mark startMark) @safe
{ {
if(!isDigit(reader_.peek())) enforce(isDigit(reader_.peek()),
{ new ScannerException("While scanning a directive", startMark,
error("While scanning a directive", startMark, expected("digit", reader_.peek()), reader_.mark));
expected("digit", reader_.peek()), reader_.mark);
return;
}
// Already found the first digit in the enforce(), so set length to 1. // Already found the first digit in the enforce(), so set length to 1.
uint length = 1; uint length = 1;
@ -997,14 +911,11 @@ struct Scanner
/// characters into that slice. /// characters into that slice.
/// ///
/// Returns: Length of tag handle (which is before tag prefix) in scanned data /// Returns: Length of tag handle (which is before tag prefix) in scanned data
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
uint scanTagDirectiveValueToSlice(const Mark startMark) @safe uint scanTagDirectiveValueToSlice(const Mark startMark) @safe
{ {
findNextNonSpace(); findNextNonSpace();
const startLength = reader_.sliceBuilder.length; const startLength = reader_.sliceBuilder.length;
scanTagDirectiveHandleToSlice(startMark); scanTagDirectiveHandleToSlice(startMark);
if(error_) { return uint.max; }
const handleLength = cast(uint)(reader_.sliceBuilder.length - startLength); const handleLength = cast(uint)(reader_.sliceBuilder.length - startLength);
findNextNonSpace(); findNextNonSpace();
scanTagDirectivePrefixToSlice(startMark); scanTagDirectivePrefixToSlice(startMark);
@ -1016,45 +927,35 @@ struct Scanner
/// ///
/// Assumes that the caller is building a slice in Reader, and puts the scanned /// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice. /// characters into that slice.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
void scanTagDirectiveHandleToSlice(const Mark startMark) @safe void scanTagDirectiveHandleToSlice(const Mark startMark) @safe
{ {
scanTagHandleToSlice!"directive"(startMark); scanTagHandleToSlice!"directive"(startMark);
if(error_) { return; } enforce(reader_.peekByte() == ' ',
if(reader_.peekByte() == ' ') { return; } new ScannerException("While scanning a directive handle", startMark,
error("While scanning a directive handle", startMark, expected("' '", reader_.peek()), reader_.mark));
expected("' '", reader_.peek()), reader_.mark);
} }
/// Scan prefix of a tag directive. /// Scan prefix of a tag directive.
/// ///
/// Assumes that the caller is building a slice in Reader, and puts the scanned /// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice. /// characters into that slice.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
void scanTagDirectivePrefixToSlice(const Mark startMark) @safe void scanTagDirectivePrefixToSlice(const Mark startMark) @safe
{ {
scanTagURIToSlice!"directive"(startMark); scanTagURIToSlice!"directive"(startMark);
if(reader_.peek().among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029')) { return; } enforce(reader_.peek().among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029'),
error("While scanning a directive prefix", startMark, new ScannerException("While scanning a directive prefix", startMark,
expected("' '", reader_.peek()), reader_.mark); expected("' '", reader_.peek()), reader_.mark));
} }
/// Scan (and ignore) ignored line after a directive. /// Scan (and ignore) ignored line after a directive.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
void scanDirectiveIgnoredLine(const Mark startMark) @safe void scanDirectiveIgnoredLine(const Mark startMark) @safe
{ {
findNextNonSpace(); findNextNonSpace();
if(reader_.peekByte() == '#') { scanToNextBreak(); } if(reader_.peekByte() == '#') { scanToNextBreak(); }
if(reader_.peek().isBreak) enforce(reader_.peek().isBreak,
{ new ScannerException("While scanning a directive", startMark,
expected("comment or a line break", reader_.peek()), reader_.mark));
scanLineBreak(); scanLineBreak();
return;
}
error("While scanning a directive", startMark,
expected("comment or a line break", reader_.peek()), reader_.mark);
} }
@ -1068,8 +969,6 @@ struct Scanner
/// and /// and
/// [ *alias , "value" ] /// [ *alias , "value" ]
/// Therefore we restrict aliases to ASCII alphanumeric characters. /// Therefore we restrict aliases to ASCII alphanumeric characters.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
Token scanAnchor(const TokenID id) @safe Token scanAnchor(const TokenID id) @safe
{ {
const startMark = reader_.mark; const startMark = reader_.mark;
@ -1080,17 +979,13 @@ struct Scanner
else { scanAlphaNumericToSlice!"an anchor"(startMark); } else { scanAlphaNumericToSlice!"an anchor"(startMark); }
// On error, value is discarded as we return immediately // On error, value is discarded as we return immediately
char[] value = reader_.sliceBuilder.finish(); char[] value = reader_.sliceBuilder.finish();
if(error_) { return Token.init; }
if(!reader_.peek().isWhiteSpace &&
!reader_.peekByte().among!('?', ':', ',', ']', '}', '%', '@'))
{
enum anchorCtx = "While scanning an anchor"; enum anchorCtx = "While scanning an anchor";
enum aliasCtx = "While scanning an alias"; enum aliasCtx = "While scanning an alias";
error(i == '*' ? aliasCtx : anchorCtx, startMark, enforce(reader_.peek().isWhiteSpace ||
expected("alphanumeric, '-' or '_'", reader_.peek()), reader_.mark); reader_.peekByte().among!('?', ':', ',', ']', '}', '%', '@'),
return Token.init; new ScannerException(i == '*' ? aliasCtx : anchorCtx, startMark,
} expected("alphanumeric, '-' or '_'", reader_.peek()), reader_.mark));
if(id == TokenID.alias_) if(id == TokenID.alias_)
{ {
@ -1104,8 +999,6 @@ struct Scanner
} }
/// Scan a tag token. /// Scan a tag token.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
Token scanTag() @safe Token scanTag() @safe
{ {
const startMark = reader_.mark; const startMark = reader_.mark;
@ -1123,13 +1016,9 @@ struct Scanner
handleEnd = 0; handleEnd = 0;
scanTagURIToSlice!"tag"(startMark); scanTagURIToSlice!"tag"(startMark);
if(error_) { return Token.init; } enforce(reader_.peekByte() == '>',
if(reader_.peekByte() != '>') new ScannerException("While scanning a tag", startMark,
{ expected("'>'", reader_.peek()), reader_.mark));
error("While scanning a tag", startMark,
expected("'>'", reader_.peek()), reader_.mark);
return Token.init;
}
reader_.forward(); reader_.forward();
} }
else if(c.isWhiteSpace) else if(c.isWhiteSpace)
@ -1158,7 +1047,6 @@ struct Scanner
{ {
scanTagHandleToSlice!"tag"(startMark); scanTagHandleToSlice!"tag"(startMark);
handleEnd = cast(uint)reader_.sliceBuilder.length; handleEnd = cast(uint)reader_.sliceBuilder.length;
if(error_) { return Token.init; }
} }
else else
{ {
@ -1168,23 +1056,17 @@ struct Scanner
} }
scanTagURIToSlice!"tag"(startMark); scanTagURIToSlice!"tag"(startMark);
if(error_) { return Token.init; }
} }
if(reader_.peek().isBreakOrSpace) enforce(reader_.peek().isBreakOrSpace,
{ new ScannerException("While scanning a tag", startMark, expected("' '", reader_.peek()),
reader_.mark));
char[] slice = reader_.sliceBuilder.finish(); char[] slice = reader_.sliceBuilder.finish();
return tagToken(startMark, reader_.mark, slice, handleEnd); return tagToken(startMark, reader_.mark, slice, handleEnd);
} }
error("While scanning a tag", startMark, expected("' '", reader_.peek()),
reader_.mark);
return Token.init;
}
/// Scan a block scalar token with specified style. /// Scan a block scalar token with specified style.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
Token scanBlockScalar(const ScalarStyle style) @safe Token scanBlockScalar(const ScalarStyle style) @safe
{ {
const startMark = reader_.mark; const startMark = reader_.mark;
@ -1193,12 +1075,10 @@ struct Scanner
reader_.forward(); reader_.forward();
const indicators = scanBlockScalarIndicators(startMark); const indicators = scanBlockScalarIndicators(startMark);
if(error_) { return Token.init; }
const chomping = indicators[0]; const chomping = indicators[0];
const increment = indicators[1]; const increment = indicators[1];
scanBlockScalarIgnoredLine(startMark); scanBlockScalarIgnoredLine(startMark);
if(error_) { return Token.init; }
// Determine the indentation level and go to the first non-empty line. // Determine the indentation level and go to the first non-empty line.
Mark endMark; Mark endMark;
@ -1325,8 +1205,6 @@ struct Scanner
} }
/// Scan chomping and indentation indicators of a scalar token. /// Scan chomping and indentation indicators of a scalar token.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
Tuple!(Chomping, int) scanBlockScalarIndicators(const Mark startMark) @safe Tuple!(Chomping, int) scanBlockScalarIndicators(const Mark startMark) @safe
{ {
auto chomping = Chomping.clip; auto chomping = Chomping.clip;
@ -1337,23 +1215,19 @@ struct Scanner
if(getChomping(c, chomping)) if(getChomping(c, chomping))
{ {
getIncrement(c, increment, startMark); getIncrement(c, increment, startMark);
if(error_) { return tuple(Chomping.init, int.max); }
} }
else else
{ {
const gotIncrement = getIncrement(c, increment, startMark); const gotIncrement = getIncrement(c, increment, startMark);
if(error_) { return tuple(Chomping.init, int.max); }
if(gotIncrement) { getChomping(c, chomping); } if(gotIncrement) { getChomping(c, chomping); }
} }
if(c.among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029')) enforce(c.among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029'),
{ new ScannerException("While scanning a block scalar", startMark,
expected("chomping or indentation indicator", c), reader_.mark));
return tuple(chomping, increment); return tuple(chomping, increment);
} }
error("While scanning a block scalar", startMark,
expected("chomping or indentation indicator", c), reader_.mark);
return tuple(Chomping.init, int.max);
}
/// Get chomping indicator, if detected. Return false otherwise. /// Get chomping indicator, if detected. Return false otherwise.
/// ///
@ -1383,40 +1257,33 @@ struct Scanner
/// the next character in the Reader. /// the next character in the Reader.
/// increment = Write the increment value here, if detected. /// increment = Write the increment value here, if detected.
/// startMark = Mark for error messages. /// startMark = Mark for error messages.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
bool getIncrement(ref dchar c, ref int increment, const Mark startMark) @safe bool getIncrement(ref dchar c, ref int increment, const Mark startMark) @safe
{ {
if(!c.isDigit) { return false; } if(!c.isDigit) { return false; }
// Convert a digit to integer. // Convert a digit to integer.
increment = c - '0'; increment = c - '0';
assert(increment < 10 && increment >= 0, "Digit has invalid value"); assert(increment < 10 && increment >= 0, "Digit has invalid value");
if(increment > 0)
{ enforce(increment > 0,
new ScannerException("While scanning a block scalar", startMark,
expected("indentation indicator in range 1-9", "0"), reader_.mark));
reader_.forward(); reader_.forward();
c = reader_.peek(); c = reader_.peek();
return true; return true;
} }
error("While scanning a block scalar", startMark,
expected("indentation indicator in range 1-9", "0"), reader_.mark);
return false;
}
/// Scan (and ignore) ignored line in a block scalar. /// Scan (and ignore) ignored line in a block scalar.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
void scanBlockScalarIgnoredLine(const Mark startMark) @safe void scanBlockScalarIgnoredLine(const Mark startMark) @safe
{ {
findNextNonSpace(); findNextNonSpace();
if(reader_.peekByte()== '#') { scanToNextBreak(); } if(reader_.peekByte()== '#') { scanToNextBreak(); }
if(reader_.peek().isBreak) enforce(reader_.peek().isBreak,
{ new ScannerException("While scanning a block scalar", startMark,
expected("comment or line break", reader_.peek()), reader_.mark));
scanLineBreak(); scanLineBreak();
return;
}
error("While scanning a block scalar", startMark,
expected("comment or line break", reader_.peek()), reader_.mark);
} }
/// Scan indentation in a block scalar, returning line breaks, max indent and end mark. /// Scan indentation in a block scalar, returning line breaks, max indent and end mark.
@ -1463,25 +1330,19 @@ struct Scanner
} }
/// Scan a qouted flow scalar token with specified quotes. /// Scan a qouted flow scalar token with specified quotes.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
Token scanFlowScalar(const ScalarStyle quotes) @safe Token scanFlowScalar(const ScalarStyle quotes) @safe
{ {
const startMark = reader_.mark; const startMark = reader_.mark;
const quote = reader_.get(); const quote = reader_.get();
reader_.sliceBuilder.begin(); reader_.sliceBuilder.begin();
scope(exit) if(error_) { reader_.sliceBuilder.finish(); }
scanFlowScalarNonSpacesToSlice(quotes, startMark); scanFlowScalarNonSpacesToSlice(quotes, startMark);
if(error_) { return Token.init; }
while(reader_.peek() != quote) while(reader_.peek() != quote)
{ {
scanFlowScalarSpacesToSlice(startMark); scanFlowScalarSpacesToSlice(startMark);
if(error_) { return Token.init; }
scanFlowScalarNonSpacesToSlice(quotes, startMark); scanFlowScalarNonSpacesToSlice(quotes, startMark);
if(error_) { return Token.init; }
} }
reader_.forward(); reader_.forward();
@ -1493,8 +1354,6 @@ struct Scanner
/// ///
/// Assumes that the caller is building a slice in Reader, and puts the scanned /// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice. /// characters into that slice.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
void scanFlowScalarNonSpacesToSlice(const ScalarStyle quotes, const Mark startMark) void scanFlowScalarNonSpacesToSlice(const ScalarStyle quotes, const Mark startMark)
@safe @safe
{ {
@ -1510,12 +1369,10 @@ struct Scanner
// This will not necessarily make slice 32 chars longer, as not all // This will not necessarily make slice 32 chars longer, as not all
// code points are 1 char. // code points are 1 char.
const char[] slice = reader_.slice(numCodePoints + 32); const char[] slice = reader_.slice(numCodePoints + 32);
if(slice.length == oldSliceLength) enforce(slice.length != oldSliceLength,
{ new ScannerException("While reading a flow scalar", startMark,
error("While reading a flow scalar", startMark, "reached end of file", reader_.mark));
"reached end of file", reader_.mark);
return;
}
for(size_t i = oldSliceLength; i < slice.length;) for(size_t i = oldSliceLength; i < slice.length;)
{ {
// slice is UTF-8 - need to decode // slice is UTF-8 - need to decode
@ -1558,45 +1415,34 @@ struct Scanner
const hexLength = dyaml.escapes.escapeHexLength(c); const hexLength = dyaml.escapes.escapeHexLength(c);
reader_.forward(); reader_.forward();
foreach(i; 0 .. hexLength) if(!reader_.peek(i).isHexDigit) foreach(i; 0 .. hexLength) {
{ enforce(reader_.peek(i).isHexDigit,
error("While scanning a double quoted scalar", startMark, new ScannerException("While scanning a double quoted scalar", startMark,
expected("escape sequence of hexadecimal numbers", expected("escape sequence of hexadecimal numbers",
reader_.peek(i)), reader_.mark); reader_.peek(i)), reader_.mark));
return;
} }
char[] hex = reader_.get(hexLength); char[] hex = reader_.get(hexLength);
enforce((hex.length > 0) && (hex.length <= 8),
new ScannerException("While scanning a double quoted scalar", startMark,
"overflow when parsing an escape sequence of " ~
"hexadecimal numbers.", reader_.mark));
char[2] escapeStart = ['\\', cast(char) c]; char[2] escapeStart = ['\\', cast(char) c];
reader_.sliceBuilder.write(escapeStart); reader_.sliceBuilder.write(escapeStart);
reader_.sliceBuilder.write(hex); reader_.sliceBuilder.write(hex);
// Note: This is just error checking; Parser does the actual
// escaping (otherwise we could accidentally create an
// escape sequence here that wasn't in input, breaking the
// escaping code in parser, which is in parser because it
// can't always be done in place)
try {
parse!int(hex, 16u);
}
catch (Exception)
{
error("While scanning a double quoted scalar", startMark,
"overflow when parsing an escape sequence of " ~
"hexadecimal numbers.", reader_.mark);
return;
}
} }
else if(c.among!('\n', '\r', '\u0085', '\u2028', '\u2029')) else if(c.among!('\n', '\r', '\u0085', '\u2028', '\u2029'))
{ {
scanLineBreak(); scanLineBreak();
scanFlowScalarBreaksToSlice(startMark); scanFlowScalarBreaksToSlice(startMark);
if(error_) { return; }
} }
else else
{ {
error("While scanning a double quoted scalar", startMark, throw new ScannerException("While scanning a double quoted scalar", startMark,
buildMsg("found unsupported escape character", c), text("found unsupported escape character ", c),
reader_.mark); reader_.mark);
return;
} }
} }
else { return; } else { return; }
@ -1607,8 +1453,6 @@ struct Scanner
/// ///
/// Assumes that the caller is building a slice in Reader, and puts the scanned /// Assumes that the caller is building a slice in Reader, and puts the scanned
/// spaces into that slice. /// spaces into that slice.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
void scanFlowScalarSpacesToSlice(const Mark startMark) @safe void scanFlowScalarSpacesToSlice(const Mark startMark) @safe
{ {
// Increase length as long as we see whitespace. // Increase length as long as we see whitespace.
@ -1618,12 +1462,9 @@ struct Scanner
// Can check the last byte without striding because '\0' is ASCII // Can check the last byte without striding because '\0' is ASCII
const c = reader_.peek(length); const c = reader_.peek(length);
if(c == '\0') enforce(c != '\0',
{ new ScannerException("While scanning a quoted scalar", startMark,
error("While scanning a quoted scalar", startMark, "found unexpected end of buffer", reader_.mark));
"found unexpected end of buffer", reader_.mark);
return;
}
// Spaces not followed by a line break. // Spaces not followed by a line break.
if(!c.among!('\n', '\r', '\u0085', '\u2028', '\u2029')) if(!c.among!('\n', '\r', '\u0085', '\u2028', '\u2029'))
@ -1642,7 +1483,6 @@ struct Scanner
// If we have extra line breaks after the first, scan them into the // If we have extra line breaks after the first, scan them into the
// slice. // slice.
const bool extraBreaks = scanFlowScalarBreaksToSlice(startMark); const bool extraBreaks = scanFlowScalarBreaksToSlice(startMark);
if(error_) { return; }
// No extra breaks, one normal line break. Replace it with a space. // No extra breaks, one normal line break. Replace it with a space.
if(lineBreak == '\n' && !extraBreaks) { reader_.sliceBuilder.write(' '); } if(lineBreak == '\n' && !extraBreaks) { reader_.sliceBuilder.write(' '); }
@ -1652,8 +1492,6 @@ struct Scanner
/// ///
/// Assumes that the caller is building a slice in Reader, and puts the scanned /// Assumes that the caller is building a slice in Reader, and puts the scanned
/// line breaks into that slice. /// line breaks into that slice.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
bool scanFlowScalarBreaksToSlice(const Mark startMark) @safe bool scanFlowScalarBreaksToSlice(const Mark startMark) @safe
{ {
// True if at least one line break was found. // True if at least one line break was found.
@ -1662,13 +1500,10 @@ struct Scanner
{ {
// Instead of checking indentation, we check for document separators. // Instead of checking indentation, we check for document separators.
const prefix = reader_.prefix(3); const prefix = reader_.prefix(3);
if((prefix == "---" || prefix == "...") && enforce(!(prefix == "---" || prefix == "...") ||
reader_.peek(3).isWhiteSpace) !reader_.peek(3).isWhiteSpace,
{ new ScannerException("While scanning a quoted scalar", startMark,
error("While scanning a quoted scalar", startMark, "found unexpected document separator", reader_.mark));
"found unexpected document separator", reader_.mark);
return false;
}
// Skip any whitespaces. // Skip any whitespaces.
while(reader_.peekByte().among!(' ', '\t')) { reader_.forward(); } while(reader_.peekByte().among!(' ', '\t')) { reader_.forward(); }
@ -1684,8 +1519,6 @@ struct Scanner
} }
/// Scan plain scalar token (no block, no quotes). /// Scan plain scalar token (no block, no quotes).
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
Token scanPlain() @safe Token scanPlain() @safe
{ {
// We keep track of the allowSimpleKey_ flag here. // We keep track of the allowSimpleKey_ flag here.
@ -1738,20 +1571,13 @@ struct Scanner
} }
// It's not clear what we should do with ':' in the flow context. // It's not clear what we should do with ':' in the flow context.
if(flowLevel_ > 0 && c == ':' && enforce(flowLevel_ == 0 || c != ':' ||
!reader_.peek(length + 1).isWhiteSpace && reader_.peek(length + 1).isWhiteSpace ||
!reader_.peek(length + 1).among!(',', '[', ']', '{', '}')) reader_.peek(length + 1).among!(',', '[', ']', '{', '}'),
{ new ScannerException("While scanning a plain scalar", startMark,
// This is an error; throw the slice away.
spacesTransaction.commit();
reader_.sliceBuilder.finish();
reader_.forward(length);
error("While scanning a plain scalar", startMark,
"found unexpected ':' . Please check " ~ "found unexpected ':' . Please check " ~
"http://pyyaml.org/wiki/YAMLColonInFlowContext for details.", "http://pyyaml.org/wiki/YAMLColonInFlowContext for details.",
reader_.mark); reader_.mark));
return Token.init;
}
if(length == 0) { break; } if(length == 0) { break; }
@ -1842,17 +1668,12 @@ struct Scanner
/// ///
/// Assumes that the caller is building a slice in Reader, and puts the scanned /// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice. /// characters into that slice.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
void scanTagHandleToSlice(string name)(const Mark startMark) void scanTagHandleToSlice(string name)(const Mark startMark)
{ {
dchar c = reader_.peek(); dchar c = reader_.peek();
enum contextMsg = "While scanning a " ~ name; enum contextMsg = "While scanning a " ~ name;
if(c != '!') enforce(c == '!',
{ new ScannerException(contextMsg, startMark, expected("'!'", c), reader_.mark));
error(contextMsg, startMark, expected("'!'", c), reader_.mark);
return;
}
uint length = 1; uint length = 1;
c = reader_.peek(length); c = reader_.peek(length);
@ -1863,12 +1684,8 @@ struct Scanner
++length; ++length;
c = reader_.peek(length); c = reader_.peek(length);
} }
if(c != '!') enforce(c == '!',
{ new ScannerException(contextMsg, startMark, expected("'!'", c), reader_.mark));
reader_.forward(length);
error(contextMsg, startMark, expected("'!'", c), reader_.mark);
return;
}
++length; ++length;
} }
@ -1879,8 +1696,6 @@ struct Scanner
/// ///
/// Assumes that the caller is building a slice in Reader, and puts the scanned /// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice. /// characters into that slice.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
void scanTagURIToSlice(string name)(const Mark startMark) void scanTagURIToSlice(string name)(const Mark startMark)
{ {
// Note: we do not check if URI is well-formed. // Note: we do not check if URI is well-formed.
@ -1896,7 +1711,6 @@ struct Scanner
reader_.sliceBuilder.write(chars); reader_.sliceBuilder.write(chars);
length = 0; length = 0;
scanURIEscapesToSlice!name(startMark); scanURIEscapesToSlice!name(startMark);
if(error_) { return; }
} }
else { ++length; } else { ++length; }
c = reader_.peek(length); c = reader_.peek(length);
@ -1909,10 +1723,9 @@ struct Scanner
} }
} }
// OK if we scanned something, error otherwise. // OK if we scanned something, error otherwise.
if(reader_.sliceBuilder.length > startLen) { return; }
enum contextMsg = "While parsing a " ~ name; enum contextMsg = "While parsing a " ~ name;
error(contextMsg, startMark, expected("URI", c), reader_.mark); enforce(reader_.sliceBuilder.length > startLen,
new ScannerException(contextMsg, startMark, expected("URI", c), reader_.mark));
} }
// Not @nogc yet because std.utf.decode is not @nogc // Not @nogc yet because std.utf.decode is not @nogc
@ -1920,8 +1733,6 @@ struct Scanner
/// ///
/// Assumes that the caller is building a slice in Reader, and puts the scanned /// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice. /// characters into that slice.
///
/// In case of an error, error_ is set. Use throwIfError() to handle this.
void scanURIEscapesToSlice(string name)(const Mark startMark) void scanURIEscapesToSlice(string name)(const Mark startMark)
{ {
import core.exception : UnicodeException; import core.exception : UnicodeException;
@ -1935,13 +1746,12 @@ struct Scanner
{ {
reader_.forward(); reader_.forward();
char[2] nextByte = [reader_.peekByte(), reader_.peekByte(1)]; char[2] nextByte = [reader_.peekByte(), reader_.peekByte(1)];
if(!nextByte[0].isHexDigit || !nextByte[1].isHexDigit)
{ enforce(nextByte[0].isHexDigit && nextByte[1].isHexDigit,
auto msg = expected("URI escape sequence of 2 hexadecimal " ~ new ScannerException(contextMsg, startMark,
"numbers", nextByte); expected("URI escape sequence of 2 hexadecimal " ~
error(contextMsg, startMark, msg, reader_.mark); "numbers", nextByte), reader_.mark));
return;
}
buffer ~= nextByte[].to!ubyte(16); buffer ~= nextByte[].to!ubyte(16);
reader_.forward(2); reader_.forward(2);
@ -1955,10 +1765,9 @@ struct Scanner
} }
catch (UnicodeException) catch (UnicodeException)
{ {
error(contextMsg, startMark, throw new ScannerException(contextMsg, startMark,
"Invalid UTF-8 data encoded in URI escape sequence", "Invalid UTF-8 data encoded in URI escape sequence",
reader_.mark); reader_.mark);
return;
} }
} }