diff --git a/source/dyaml/constructor.d b/source/dyaml/constructor.d index b302d21..bc1d75c 100644 --- a/source/dyaml/constructor.d +++ b/source/dyaml/constructor.d @@ -38,7 +38,7 @@ class ConstructorException : YAMLException /// Params: msg = Error message. /// start = Start 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 { super(msg ~ "\nstart: " ~ start.toString() ~ "\nend: " ~ end.toString(), diff --git a/source/dyaml/node.d b/source/dyaml/node.d index b7c707c..b1690d2 100644 --- a/source/dyaml/node.d +++ b/source/dyaml/node.d @@ -33,7 +33,7 @@ class NodeException : YAMLException // // Params: msg = Error message. // 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 { super(msg ~ "\nNode at: " ~ start.toString(), file, line); diff --git a/source/dyaml/reader.d b/source/dyaml/reader.d index dbe0c2f..28c848e 100644 --- a/source/dyaml/reader.d +++ b/source/dyaml/reader.d @@ -34,7 +34,7 @@ package: ///Exception thrown at Reader errors. 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 { super("Reader error: " ~ msg, file, line); diff --git a/source/dyaml/scanner.d b/source/dyaml/scanner.d index a691552..39e1099 100644 --- a/source/dyaml/scanner.d +++ b/source/dyaml/scanner.d @@ -150,25 +150,6 @@ struct Scanner /// Possible simple keys indexed by flow levels. 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: /// Construct a Scanner using specified Reader. this(Reader reader) @safe nothrow @@ -203,45 +184,11 @@ struct Scanner } 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 /// function. string expected(T)(string expected, T found) { - return buildMsg("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); + return text("expected ", expected, ", but found ", found); } /// Determine whether or not we need to fetch more tokens before peeking/getting a token. @@ -462,7 +409,6 @@ struct Scanner allowSimpleKey_ = false; auto directive = scanDirective(); - throwIfError(); tokens_.push(directive); } @@ -646,7 +592,6 @@ struct Scanner allowSimpleKey_ = false; auto anchor = scanAnchor(id); - throwIfError(); tokens_.push(anchor); } @@ -663,7 +608,6 @@ struct Scanner allowSimpleKey_ = false; tokens_.push(scanTag()); - throwIfError(); } /// Add block SCALAR token. @@ -676,7 +620,6 @@ struct Scanner allowSimpleKey_ = true; auto blockScalar = scanBlockScalar(style); - throwIfError(); tokens_.push(blockScalar); } @@ -694,7 +637,6 @@ struct Scanner // Scan and add SCALAR. auto scalar = scanFlowScalar(quotes); - throwIfError(); tokens_.push(scalar); } @@ -711,7 +653,6 @@ struct Scanner // change this flag if the scan is finished at the beginning of the line. allowSimpleKey_ = false; auto plain = scanPlain(); - throwIfError(); // Scan and add SCALAR. May change allowSimpleKey_ tokens_.push(plain); @@ -802,21 +743,14 @@ struct Scanner /// /// Assumes that the caller is building a slice in Reader, and puts the scanned /// characters into that slice. - /// - /// In case of an error, error_ is set. Use throwIfError() to handle this. void scanAlphaNumericToSlice(string name)(const Mark startMark) { size_t length; dchar c = reader_.peek(); while(c.isAlphaNum || c.among!('-', '_')) { c = reader_.peek(++length); } - if(length == 0) - { - enum contextMsg = "While scanning " ~ name; - error(contextMsg, startMark, expected("alphanumeric, '-' or '_'", c), - reader_.mark); - return; - } + enforce(length > 0, new ScannerException("While scanning " ~ name, + startMark, expected("alphanumeric, '-' or '_'", c), reader_.mark)); reader_.sliceBuilder.write(reader_.get(length)); } @@ -890,7 +824,6 @@ struct Scanner // Scan directive name reader_.sliceBuilder.begin(); scanDirectiveNameToSlice(startMark); - if(error_) { return Token.init; } const name = reader_.sliceBuilder.finish(); reader_.sliceBuilder.begin(); @@ -899,7 +832,6 @@ struct Scanner uint tagHandleEnd = uint.max; if(name == "YAML") { scanYAMLDirectiveValueToSlice(startMark); } else if(name == "TAG") { tagHandleEnd = scanTagDirectiveValueToSlice(startMark); } - if(error_) { return Token.init; } char[] value = reader_.sliceBuilder.finish(); Mark endMark = reader_.mark; @@ -914,7 +846,6 @@ struct Scanner } scanDirectiveIgnoredLine(startMark); - if(error_) { return Token.init; } 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 /// characters into that slice. - /// - /// In case of an error, error_ is set. Use throwIfError() to handle this. void scanDirectiveNameToSlice(const Mark startMark) @safe { // Scan directive name. scanAlphaNumericToSlice!"a directive"(startMark); - if(error_) { return; } - if(reader_.peek().among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029')) { return; } - error("While scanning a directive", startMark, - expected("alphanumeric, '-' or '_'", reader_.peek()), reader_.mark); + enforce(reader_.peek().among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029'), + new ScannerException("While scanning a directive", startMark, + expected("alphanumeric, '-' or '_'", reader_.peek()), reader_.mark)); } /// 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 /// characters into that slice. - /// - /// In case of an error, error_ is set. Use throwIfError() to handle this. void scanYAMLDirectiveValueToSlice(const Mark startMark) @safe { findNextNonSpace(); scanYAMLDirectiveNumberToSlice(startMark); - if(error_) { return; } - if(reader_.peekByte() != '.') - { - error("While scanning a directive", startMark, - expected("digit or '.'", reader_.peek()), reader_.mark); - return; - } + enforce(reader_.peekByte() == '.', + new ScannerException("While scanning a directive", startMark, + expected("digit or '.'", reader_.peek()), reader_.mark)); // Skip the '.'. reader_.forward(); reader_.sliceBuilder.write('.'); scanYAMLDirectiveNumberToSlice(startMark); - if(error_) { return; } - if(!reader_.peek().among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029')) - { - error("While scanning a directive", startMark, - expected("digit or '.'", reader_.peek()), reader_.mark); - } + enforce(reader_.peek().among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029'), + new ScannerException("While scanning a directive", startMark, + expected("digit or '.'", reader_.peek()), reader_.mark)); } /// Scan a number from a YAML directive. /// /// Assumes that the caller is building a slice in Reader, and puts the scanned /// characters into that slice. - /// - /// In case of an error, error_ is set. Use throwIfError() to handle this. void scanYAMLDirectiveNumberToSlice(const Mark startMark) @safe { - if(!isDigit(reader_.peek())) - { - error("While scanning a directive", startMark, - expected("digit", reader_.peek()), reader_.mark); - return; - } + enforce(isDigit(reader_.peek()), + new ScannerException("While scanning a directive", startMark, + expected("digit", reader_.peek()), reader_.mark)); // Already found the first digit in the enforce(), so set length to 1. uint length = 1; @@ -997,14 +911,11 @@ struct Scanner /// characters into that slice. /// /// 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 { findNextNonSpace(); const startLength = reader_.sliceBuilder.length; scanTagDirectiveHandleToSlice(startMark); - if(error_) { return uint.max; } const handleLength = cast(uint)(reader_.sliceBuilder.length - startLength); findNextNonSpace(); scanTagDirectivePrefixToSlice(startMark); @@ -1016,45 +927,35 @@ struct Scanner /// /// Assumes that the caller is building a slice in Reader, and puts the scanned /// characters into that slice. - /// - /// In case of an error, error_ is set. Use throwIfError() to handle this. void scanTagDirectiveHandleToSlice(const Mark startMark) @safe { scanTagHandleToSlice!"directive"(startMark); - if(error_) { return; } - if(reader_.peekByte() == ' ') { return; } - error("While scanning a directive handle", startMark, - expected("' '", reader_.peek()), reader_.mark); + enforce(reader_.peekByte() == ' ', + new ScannerException("While scanning a directive handle", startMark, + expected("' '", reader_.peek()), reader_.mark)); } /// Scan prefix of a tag directive. /// /// Assumes that the caller is building a slice in Reader, and puts the scanned /// characters into that slice. - /// - /// In case of an error, error_ is set. Use throwIfError() to handle this. void scanTagDirectivePrefixToSlice(const Mark startMark) @safe { scanTagURIToSlice!"directive"(startMark); - if(reader_.peek().among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029')) { return; } - error("While scanning a directive prefix", startMark, - expected("' '", reader_.peek()), reader_.mark); + enforce(reader_.peek().among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029'), + new ScannerException("While scanning a directive prefix", startMark, + expected("' '", reader_.peek()), reader_.mark)); } /// 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 { findNextNonSpace(); if(reader_.peekByte() == '#') { scanToNextBreak(); } - if(reader_.peek().isBreak) - { - scanLineBreak(); - return; - } - error("While scanning a directive", startMark, - expected("comment or a line break", reader_.peek()), reader_.mark); + enforce(reader_.peek().isBreak, + new ScannerException("While scanning a directive", startMark, + expected("comment or a line break", reader_.peek()), reader_.mark)); + scanLineBreak(); } @@ -1068,8 +969,6 @@ struct Scanner /// and /// [ *alias , "value" ] /// 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 { const startMark = reader_.mark; @@ -1080,17 +979,13 @@ struct Scanner else { scanAlphaNumericToSlice!"an anchor"(startMark); } // On error, value is discarded as we return immediately char[] value = reader_.sliceBuilder.finish(); - if(error_) { return Token.init; } - if(!reader_.peek().isWhiteSpace && - !reader_.peekByte().among!('?', ':', ',', ']', '}', '%', '@')) - { - enum anchorCtx = "While scanning an anchor"; - enum aliasCtx = "While scanning an alias"; - error(i == '*' ? aliasCtx : anchorCtx, startMark, - expected("alphanumeric, '-' or '_'", reader_.peek()), reader_.mark); - return Token.init; - } + enum anchorCtx = "While scanning an anchor"; + enum aliasCtx = "While scanning an alias"; + enforce(reader_.peek().isWhiteSpace || + reader_.peekByte().among!('?', ':', ',', ']', '}', '%', '@'), + new ScannerException(i == '*' ? aliasCtx : anchorCtx, startMark, + expected("alphanumeric, '-' or '_'", reader_.peek()), reader_.mark)); if(id == TokenID.alias_) { @@ -1104,8 +999,6 @@ struct Scanner } /// Scan a tag token. - /// - /// In case of an error, error_ is set. Use throwIfError() to handle this. Token scanTag() @safe { const startMark = reader_.mark; @@ -1123,13 +1016,9 @@ struct Scanner handleEnd = 0; scanTagURIToSlice!"tag"(startMark); - if(error_) { return Token.init; } - if(reader_.peekByte() != '>') - { - error("While scanning a tag", startMark, - expected("'>'", reader_.peek()), reader_.mark); - return Token.init; - } + enforce(reader_.peekByte() == '>', + new ScannerException("While scanning a tag", startMark, + expected("'>'", reader_.peek()), reader_.mark)); reader_.forward(); } else if(c.isWhiteSpace) @@ -1158,7 +1047,6 @@ struct Scanner { scanTagHandleToSlice!"tag"(startMark); handleEnd = cast(uint)reader_.sliceBuilder.length; - if(error_) { return Token.init; } } else { @@ -1168,23 +1056,17 @@ struct Scanner } scanTagURIToSlice!"tag"(startMark); - if(error_) { return Token.init; } } - if(reader_.peek().isBreakOrSpace) - { - char[] slice = reader_.sliceBuilder.finish(); - return tagToken(startMark, reader_.mark, slice, handleEnd); - } + enforce(reader_.peek().isBreakOrSpace, + 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; + char[] slice = reader_.sliceBuilder.finish(); + return tagToken(startMark, reader_.mark, slice, handleEnd); } /// 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 { const startMark = reader_.mark; @@ -1193,12 +1075,10 @@ struct Scanner reader_.forward(); const indicators = scanBlockScalarIndicators(startMark); - if(error_) { return Token.init; } const chomping = indicators[0]; const increment = indicators[1]; scanBlockScalarIgnoredLine(startMark); - if(error_) { return Token.init; } // Determine the indentation level and go to the first non-empty line. Mark endMark; @@ -1325,8 +1205,6 @@ struct Scanner } /// 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 { auto chomping = Chomping.clip; @@ -1337,22 +1215,18 @@ struct Scanner if(getChomping(c, chomping)) { getIncrement(c, increment, startMark); - if(error_) { return tuple(Chomping.init, int.max); } } else { const gotIncrement = getIncrement(c, increment, startMark); - if(error_) { return tuple(Chomping.init, int.max); } if(gotIncrement) { getChomping(c, chomping); } } - if(c.among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029')) - { - 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); + 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); } /// Get chomping indicator, if detected. Return false otherwise. @@ -1383,40 +1257,33 @@ struct Scanner /// the next character in the Reader. /// increment = Write the increment value here, if detected. /// 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 { if(!c.isDigit) { return false; } // Convert a digit to integer. increment = c - '0'; assert(increment < 10 && increment >= 0, "Digit has invalid value"); - if(increment > 0) - { - reader_.forward(); - c = reader_.peek(); - return true; - } - error("While scanning a block scalar", startMark, - expected("indentation indicator in range 1-9", "0"), reader_.mark); - return false; + + enforce(increment > 0, + new ScannerException("While scanning a block scalar", startMark, + expected("indentation indicator in range 1-9", "0"), reader_.mark)); + + reader_.forward(); + c = reader_.peek(); + return true; } /// 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 { findNextNonSpace(); if(reader_.peekByte()== '#') { scanToNextBreak(); } - if(reader_.peek().isBreak) - { - scanLineBreak(); - return; - } - error("While scanning a block scalar", startMark, - expected("comment or line break", reader_.peek()), reader_.mark); + enforce(reader_.peek().isBreak, + new ScannerException("While scanning a block scalar", startMark, + expected("comment or line break", reader_.peek()), reader_.mark)); + + scanLineBreak(); } /// 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. - /// - /// In case of an error, error_ is set. Use throwIfError() to handle this. Token scanFlowScalar(const ScalarStyle quotes) @safe { const startMark = reader_.mark; const quote = reader_.get(); reader_.sliceBuilder.begin(); - scope(exit) if(error_) { reader_.sliceBuilder.finish(); } scanFlowScalarNonSpacesToSlice(quotes, startMark); - if(error_) { return Token.init; } while(reader_.peek() != quote) { scanFlowScalarSpacesToSlice(startMark); - if(error_) { return Token.init; } scanFlowScalarNonSpacesToSlice(quotes, startMark); - if(error_) { return Token.init; } } reader_.forward(); @@ -1493,8 +1354,6 @@ struct Scanner /// /// Assumes that the caller is building a slice in Reader, and puts the scanned /// 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) @safe { @@ -1510,12 +1369,10 @@ struct Scanner // This will not necessarily make slice 32 chars longer, as not all // code points are 1 char. const char[] slice = reader_.slice(numCodePoints + 32); - if(slice.length == oldSliceLength) - { - error("While reading a flow scalar", startMark, - "reached end of file", reader_.mark); - return; - } + enforce(slice.length != oldSliceLength, + new ScannerException("While reading a flow scalar", startMark, + "reached end of file", reader_.mark)); + for(size_t i = oldSliceLength; i < slice.length;) { // slice is UTF-8 - need to decode @@ -1558,45 +1415,34 @@ struct Scanner const hexLength = dyaml.escapes.escapeHexLength(c); reader_.forward(); - foreach(i; 0 .. hexLength) if(!reader_.peek(i).isHexDigit) - { - error("While scanning a double quoted scalar", startMark, - expected("escape sequence of hexadecimal numbers", - reader_.peek(i)), reader_.mark); - return; + foreach(i; 0 .. hexLength) { + enforce(reader_.peek(i).isHexDigit, + new ScannerException("While scanning a double quoted scalar", startMark, + expected("escape sequence of hexadecimal numbers", + reader_.peek(i)), reader_.mark)); } 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]; reader_.sliceBuilder.write(escapeStart); 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')) { scanLineBreak(); scanFlowScalarBreaksToSlice(startMark); - if(error_) { return; } } else { - error("While scanning a double quoted scalar", startMark, - buildMsg("found unsupported escape character", c), + throw new ScannerException("While scanning a double quoted scalar", startMark, + text("found unsupported escape character ", c), reader_.mark); - return; } } else { return; } @@ -1607,8 +1453,6 @@ struct Scanner /// /// Assumes that the caller is building a slice in Reader, and puts the scanned /// spaces into that slice. - /// - /// In case of an error, error_ is set. Use throwIfError() to handle this. void scanFlowScalarSpacesToSlice(const Mark startMark) @safe { // 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 const c = reader_.peek(length); - if(c == '\0') - { - error("While scanning a quoted scalar", startMark, - "found unexpected end of buffer", reader_.mark); - return; - } + enforce(c != '\0', + new ScannerException("While scanning a quoted scalar", startMark, + "found unexpected end of buffer", reader_.mark)); // Spaces not followed by a line break. 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 // slice. const bool extraBreaks = scanFlowScalarBreaksToSlice(startMark); - if(error_) { return; } // No extra breaks, one normal line break. Replace it with a space. 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 /// line breaks into that slice. - /// - /// In case of an error, error_ is set. Use throwIfError() to handle this. bool scanFlowScalarBreaksToSlice(const Mark startMark) @safe { // True if at least one line break was found. @@ -1662,13 +1500,10 @@ struct Scanner { // Instead of checking indentation, we check for document separators. const prefix = reader_.prefix(3); - if((prefix == "---" || prefix == "...") && - reader_.peek(3).isWhiteSpace) - { - error("While scanning a quoted scalar", startMark, - "found unexpected document separator", reader_.mark); - return false; - } + enforce(!(prefix == "---" || prefix == "...") || + !reader_.peek(3).isWhiteSpace, + new ScannerException("While scanning a quoted scalar", startMark, + "found unexpected document separator", reader_.mark)); // Skip any whitespaces. while(reader_.peekByte().among!(' ', '\t')) { reader_.forward(); } @@ -1684,8 +1519,6 @@ struct Scanner } /// Scan plain scalar token (no block, no quotes). - /// - /// In case of an error, error_ is set. Use throwIfError() to handle this. Token scanPlain() @safe { // 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. - if(flowLevel_ > 0 && c == ':' && - !reader_.peek(length + 1).isWhiteSpace && - !reader_.peek(length + 1).among!(',', '[', ']', '{', '}')) - { - // 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 " ~ - "http://pyyaml.org/wiki/YAMLColonInFlowContext for details.", - reader_.mark); - return Token.init; - } + enforce(flowLevel_ == 0 || c != ':' || + reader_.peek(length + 1).isWhiteSpace || + reader_.peek(length + 1).among!(',', '[', ']', '{', '}'), + new ScannerException("While scanning a plain scalar", startMark, + "found unexpected ':' . Please check " ~ + "http://pyyaml.org/wiki/YAMLColonInFlowContext for details.", + reader_.mark)); if(length == 0) { break; } @@ -1842,17 +1668,12 @@ struct Scanner /// /// Assumes that the caller is building a slice in Reader, and puts the scanned /// characters into that slice. - /// - /// In case of an error, error_ is set. Use throwIfError() to handle this. void scanTagHandleToSlice(string name)(const Mark startMark) { dchar c = reader_.peek(); enum contextMsg = "While scanning a " ~ name; - if(c != '!') - { - error(contextMsg, startMark, expected("'!'", c), reader_.mark); - return; - } + enforce(c == '!', + new ScannerException(contextMsg, startMark, expected("'!'", c), reader_.mark)); uint length = 1; c = reader_.peek(length); @@ -1863,12 +1684,8 @@ struct Scanner ++length; c = reader_.peek(length); } - if(c != '!') - { - reader_.forward(length); - error(contextMsg, startMark, expected("'!'", c), reader_.mark); - return; - } + enforce(c == '!', + new ScannerException(contextMsg, startMark, expected("'!'", c), reader_.mark)); ++length; } @@ -1879,8 +1696,6 @@ struct Scanner /// /// Assumes that the caller is building a slice in Reader, and puts the scanned /// characters into that slice. - /// - /// In case of an error, error_ is set. Use throwIfError() to handle this. void scanTagURIToSlice(string name)(const Mark startMark) { // Note: we do not check if URI is well-formed. @@ -1896,7 +1711,6 @@ struct Scanner reader_.sliceBuilder.write(chars); length = 0; scanURIEscapesToSlice!name(startMark); - if(error_) { return; } } else { ++length; } c = reader_.peek(length); @@ -1909,10 +1723,9 @@ struct Scanner } } // OK if we scanned something, error otherwise. - if(reader_.sliceBuilder.length > startLen) { return; } - 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 @@ -1920,8 +1733,6 @@ struct Scanner /// /// Assumes that the caller is building a slice in Reader, and puts the scanned /// characters into that slice. - /// - /// In case of an error, error_ is set. Use throwIfError() to handle this. void scanURIEscapesToSlice(string name)(const Mark startMark) { import core.exception : UnicodeException; @@ -1935,13 +1746,12 @@ struct Scanner { reader_.forward(); char[2] nextByte = [reader_.peekByte(), reader_.peekByte(1)]; - if(!nextByte[0].isHexDigit || !nextByte[1].isHexDigit) - { - auto msg = expected("URI escape sequence of 2 hexadecimal " ~ - "numbers", nextByte); - error(contextMsg, startMark, msg, reader_.mark); - return; - } + + enforce(nextByte[0].isHexDigit && nextByte[1].isHexDigit, + new ScannerException(contextMsg, startMark, + expected("URI escape sequence of 2 hexadecimal " ~ + "numbers", nextByte), reader_.mark)); + buffer ~= nextByte[].to!ubyte(16); reader_.forward(2); @@ -1955,10 +1765,9 @@ struct Scanner } catch (UnicodeException) { - error(contextMsg, startMark, + throw new ScannerException(contextMsg, startMark, "Invalid UTF-8 data encoded in URI escape sequence", reader_.mark); - return; } }