Cleaned up exceptions and their messages.

Some minor dog fixes.
This commit is contained in:
Ferdinand Majerech 2011-10-18 16:12:22 +02:00
parent 009017eef0
commit 8ad650e089
24 changed files with 284 additions and 298 deletions

Binary file not shown.

View file

@ -105,6 +105,11 @@
</dt> </dt>
<dd><p>Construct a Dumper writing to a stream. This is useful to e.g. write to memory.</p> <dd><p>Construct a Dumper writing to a stream. This is useful to e.g. write to memory.</p>
</dd>
<dt class="d_decl">@property void <a name="name"></a><span class="ddoc_psymbol">name</span>(string <a name="name"></a><span class="ddoc_psymbol">name</span>);
</dt>
<dd><p>Set stream name. Used in debugging messages.</p>
</dd> </dd>
<dt class="d_decl">void <a name="resolver"></a><span class="ddoc_psymbol">resolver</span>(Resolver <a name="resolver"></a><span class="ddoc_psymbol">resolver</span>); <dt class="d_decl">void <a name="resolver"></a><span class="ddoc_psymbol">resolver</span>(Resolver <a name="resolver"></a><span class="ddoc_psymbol">resolver</span>);
</dt> </dt>

View file

@ -47,7 +47,7 @@
<dl><dt class="d_decl">this(string <b>msg</b>, string <b>file</b> = __FILE__, int <b>line</b> = __LINE__); <dl><dt class="d_decl">this(string <b>msg</b>, string <b>file</b> = __FILE__, int <b>line</b> = __LINE__);
</dt> </dt>
<dd><p>Construct a YAMLException with specified message, and position where it was thrown.</p> <dd><p>Construct a YAMLException with specified message and position where it was thrown.</p>
</dd> </dd>
</dl> </dl>

View file

@ -104,7 +104,7 @@
<b>Parameters:</b><div class="pbr"><table class=parms><tr><td valign=top>string <b>filename</b></td> <b>Parameters:</b><div class="pbr"><table class=parms><tr><td valign=top>string <b>filename</b></td>
<td valign=top>Name of the file to load from.</td></tr> <td valign=top>Name of the file to load from.</td></tr>
</table></div> </table></div>
<b>Throws:</b><div class="pbr">YAMLException if the file could not be opened or read from.</div> <b>Throws:</b><div class="pbr">YAMLException if the file could not be opened or read.</div>
</dd> </dd>
<dt class="d_decl">this(Stream <b>stream</b>); <dt class="d_decl">this(Stream <b>stream</b>);
@ -114,7 +114,7 @@
<b>Parameters:</b><div class="pbr"><table class=parms><tr><td valign=top>Stream <b>stream</b></td> <b>Parameters:</b><div class="pbr"><table class=parms><tr><td valign=top>Stream <b>stream</b></td>
<td valign=top>Stream to read from. Must be readable.</td></tr> <td valign=top>Stream to read from. Must be readable.</td></tr>
</table></div> </table></div>
<b>Throws:</b><div class="pbr">YAMLException if <b>stream</b> could not be read from.</div> <b>Throws:</b><div class="pbr">YAMLException if <b>stream</b> could not be read.</div>
</dd> </dd>
<dt class="d_decl">@property void <a name="name"></a><span class="ddoc_psymbol">name</span>(string <a name="name"></a><span class="ddoc_psymbol">name</span>); <dt class="d_decl">@property void <a name="name"></a><span class="ddoc_psymbol">name</span>(string <a name="name"></a><span class="ddoc_psymbol">name</span>);
@ -136,7 +136,7 @@
</dt> </dt>
<dd><p>Load single YAML document. <dd><p>Load single YAML document.
</p> </p>
<p>If none or more than one YAML document is found, this will throw a YAMLException. <p>If none or more than one YAML document is found, this throws a YAMLException.
</p> </p>
<b>Returns:</b><div class="pbr">Root node of the document. <b>Returns:</b><div class="pbr">Root node of the document.
@ -153,7 +153,7 @@
<b>Returns:</b><div class="pbr">Array of root nodes of all documents in the file/stream. <b>Returns:</b><div class="pbr">Array of root nodes of all documents in the file/stream.
</div> </div>
<b>Throws:</b><div class="pbr">YAMLException on a YAML parsing error.</div> <b>Throws:</b><div class="pbr">YAMLException on a parsing error.</div>
</dd> </dd>
<dt class="d_decl">int <a name="opApply"></a><span class="ddoc_psymbol">opApply</span>(int delegate(ref Node) <b>dg</b>); <dt class="d_decl">int <a name="opApply"></a><span class="ddoc_psymbol">opApply</span>(int delegate(ref Node) <b>dg</b>);

View file

@ -69,13 +69,13 @@
</dt> </dt>
<dd><p>Add a function to represent nodes with a specific data type. <dd><p>Add a function to represent nodes with a specific data type.
</p> </p>
<p>The representer function takes a reference to a Node storing the data <p>The representer function takes references to a Node storing the data
type and to the Representer. It returns the represented node and may type and to the Representer. It returns the represented node and may
throw a RepresenterException. See the example for more information. throw a RepresenterException. See the example for more information.
<br> <br>
Only one function may be specified for one data type. Default data Only one function may be specified for one data type. Default data
types already have representer functions unless disabled in these types already have representer functions unless disabled in the
Representer constructor. Representer constructor.
</p> </p>

View file

@ -138,7 +138,7 @@ struct appears in Phobos.</p>
</div> </div>
<div class="footer"> <div class="footer">
&copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov. &copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov.
Last updated on Oct 17, 2011. Last updated on Oct 18, 2011.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7. Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7.
</div> </div>
</body> </body>

View file

@ -104,7 +104,7 @@
</div> </div>
<div class="footer"> <div class="footer">
&copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov. &copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov.
Last updated on Oct 17, 2011. Last updated on Oct 18, 2011.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7. Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7.
</div> </div>
</body> </body>

View file

@ -87,7 +87,7 @@
</div> </div>
<div class="footer"> <div class="footer">
&copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov. &copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov.
Last updated on Oct 17, 2011. Last updated on Oct 18, 2011.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7. Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7.
</div> </div>
</body> </body>

View file

@ -369,7 +369,7 @@ directory of the D:YAML package.</p>
</div> </div>
<div class="footer"> <div class="footer">
&copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov. &copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov.
Last updated on Oct 17, 2011. Last updated on Oct 18, 2011.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7. Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7.
</div> </div>
</body> </body>

View file

@ -237,7 +237,7 @@ example in the <tt class="docutils literal"><span class="pre">example/getting_st
</div> </div>
<div class="footer"> <div class="footer">
&copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov. &copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov.
Last updated on Oct 17, 2011. Last updated on Oct 18, 2011.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7. Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7.
</div> </div>
</body> </body>

View file

@ -330,7 +330,7 @@ Some of these might change in the future (especially !!map and !!set).</p>
</div> </div>
<div class="footer"> <div class="footer">
&copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov. &copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov.
Last updated on Oct 17, 2011. Last updated on Oct 18, 2011.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7. Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7.
</div> </div>
</body> </body>

View file

@ -26,7 +26,7 @@ import dyaml.resolver;
package: package:
/** /**
* Marked exception thrown at composer errors. * Exception thrown at composer errors.
* *
* See_Also: MarkedYAMLException * See_Also: MarkedYAMLException
*/ */
@ -113,7 +113,7 @@ final class Composer
//Ensure that the stream contains no more documents. //Ensure that the stream contains no more documents.
enforce(parser_.checkEvent(EventID.StreamEnd), enforce(parser_.checkEvent(EventID.StreamEnd),
new ComposerException("Expected single document in the stream, " new ComposerException("Expected single document in the stream, "
"but found another document: ", "but found another document.",
parser_.getEvent().startMark)); parser_.getEvent().startMark));
//Drop the STREAM-END event. //Drop the STREAM-END event.
@ -246,6 +246,18 @@ final class Composer
{ {
Node.Pair[] result; Node.Pair[] result;
void error(Node node)
{
//this is Composer, but the code is related to Constructor.
throw new ConstructorException("While constructing a mapping, "
"expected a mapping or a list of "
"mappings for merging, but found: "
~ node.type.toString ~
" NOTE: line/column shows topmost parent "
"to which the content is being merged",
startMark, endMark);
}
if(root.isMapping) if(root.isMapping)
{ {
Node[] toMerge; Node[] toMerge;
@ -259,32 +271,15 @@ final class Composer
merge(result, flatten(node, startMark, endMark)); merge(result, flatten(node, startMark, endMark));
} }
} }
else if(root.isSequence)
{
//Must be a sequence of mappings. //Must be a sequence of mappings.
foreach(ref Node node; root) else if(root.isSequence) foreach(ref Node node; root)
{ {
//this is Composer, but the code is related to constructor if(!node.isType!(Node.Pair[])){error(node);}
enforce(node.isType!(Node.Pair[]),
new ConstructorException("While constructing a mapping, " ~
"expected a mapping for merging, but found"
~ node.type.toString ~
"NOTE: line/column shows topmost parent "
"to which the content is being merged",
startMark, endMark));
merge(result, flatten(node, startMark, endMark)); merge(result, flatten(node, startMark, endMark));
} }
}
else else
{ {
//this is Composer, but the code is related to constructor error(root);
throw new ConstructorException("While constructing a mapping, " ~
"expected a mapping or a list of mappings for "
"merging, but found: "
~ root.type.toString ~
"NOTE: line/column shows topmost parent "
"to which the content is being merged",
startMark, endMark);
} }
return result; return result;

View file

@ -45,11 +45,13 @@ class ConstructorException : YAMLException
*/ */
this(string msg, Mark start, Mark end, string file = __FILE__, int line = __LINE__) this(string msg, Mark start, Mark end, string file = __FILE__, int line = __LINE__)
{ {
super(msg ~ "\nstart:" ~ start.toString() ~ "\nend:" ~ end.toString(), super(msg ~ "\nstart: " ~ start.toString() ~ "\nend: " ~ end.toString(),
file, line); file, line);
} }
} }
private alias ConstructorException Error;
/** /**
* Constructs YAML values. * Constructs YAML values.
* *
@ -177,8 +179,7 @@ final class Constructor
if(is(T : string) || is(T == Node[]) || is(T == Node.Pair[])) if(is(T : string) || is(T == Node[]) || is(T == Node.Pair[]))
{ {
enforce((tag in *delegates!T) !is null, enforce((tag in *delegates!T) !is null,
new ConstructorException("Could not determine a constructor for tag " new Error("No constructor function for tag " ~ tag.get(), start, end));
~ tag.get(), start, end));
Node node = Node(value); Node node = Node(value);
return Node.rawNode((*delegates!T)[tag](start, end, node), start, tag); return Node.rawNode((*delegates!T)[tag](start, end, node), start, tag);
} }
@ -234,7 +235,7 @@ bool constructBool(Mark start, Mark end, ref Node node)
string value = node.get!string().toLower(); string value = node.get!string().toLower();
if(["yes", "true", "on"].canFind(value)) {return true;} if(["yes", "true", "on"].canFind(value)) {return true;}
if(["no", "false", "off"].canFind(value)){return false;} if(["no", "false", "off"].canFind(value)){return false;}
throw new ConstructorException("Unable to parse boolean value: " ~ value, start, end); throw new Error("Unable to parse boolean value: " ~ value, start, end);
} }
///Construct an integer (long) node. ///Construct an integer (long) node.
@ -248,8 +249,7 @@ long constructLong(Mark start, Mark end, ref Node node)
value = value[1 .. $]; value = value[1 .. $];
} }
enforce(value != "", new ConstructorException("Unable to parse float value: " ~ value, enforce(value != "", new Error("Unable to parse float value: " ~ value, start, end));
start, end));
long result; long result;
try try
@ -279,7 +279,7 @@ long constructLong(Mark start, Mark end, ref Node node)
} }
catch(ConvException e) catch(ConvException e)
{ {
throw new ConstructorException("Unable to parse integer value: " ~ value, start, end); throw new Error("Unable to parse integer value: " ~ value, start, end);
} }
return result; return result;
@ -318,7 +318,7 @@ real constructReal(Mark start, Mark end, ref Node node)
} }
enforce(value != "" && value != "nan" && value != "inf" && value != "-inf", enforce(value != "" && value != "nan" && value != "inf" && value != "-inf",
new ConstructorException("Unable to parse float value: " ~ value, start, end)); new Error("Unable to parse float value: " ~ value, start, end));
real result; real result;
try try
@ -344,7 +344,7 @@ real constructReal(Mark start, Mark end, ref Node node)
} }
catch(ConvException e) catch(ConvException e)
{ {
throw new ConstructorException("Unable to parse float value: " ~ value, start, end); throw new Error("Unable to parse float value: " ~ value, start, end);
} }
return result; return result;
@ -386,13 +386,13 @@ ubyte[] constructBinary(Mark start, Mark end, ref Node node)
try{return Base64.decode(value.removechars("\n"));} try{return Base64.decode(value.removechars("\n"));}
catch(Exception e) catch(Exception e)
{ {
throw new ConstructorException("Unable to decode base64 value: " ~ e.msg, start, throw new Error("Unable to decode base64 value: " ~ e.msg,
end); start, end);
} }
} }
catch(UtfException e) catch(UtfException e)
{ {
throw new ConstructorException("Unable to decode base64 value: " ~ e.msg, start, end); throw new Error("Unable to decode base64 value: " ~ e.msg, start, end);
} }
} }
unittest unittest
@ -419,8 +419,8 @@ SysTime constructTimestamp(Mark start, Mark end, ref Node node)
//First, get year, month and day. //First, get year, month and day.
auto matches = match(value, YMDRegexp); auto matches = match(value, YMDRegexp);
enforce(!matches.empty, new ConstructorException("Unable to parse timestamp value: " enforce(!matches.empty,
~ value, start, end)); new Error("Unable to parse timestamp value: " ~ value, start, end));
auto captures = matches.front.captures; auto captures = matches.front.captures;
const year = to!int(captures[1]); const year = to!int(captures[1]);
@ -467,13 +467,11 @@ SysTime constructTimestamp(Mark start, Mark end, ref Node node)
} }
catch(ConvException e) catch(ConvException e)
{ {
throw new ConstructorException("Unable to parse timestamp value: " ~ value ~ throw new Error("Unable to parse timestamp value " ~ value ~ " : " ~ e.msg, start, end);
" Reason: " ~ e.msg, start, end);
} }
catch(DateTimeException e) catch(DateTimeException e)
{ {
throw new ConstructorException("Invalid timestamp value: " ~ value ~ throw new Error("Invalid timestamp value " ~ value ~ " : " ~ e.msg, start, end);
" Reason: " ~ e.msg, start, end);
} }
assert(false, "This code should never be reached"); assert(false, "This code should never be reached");
@ -520,9 +518,8 @@ Node.Pair[] getPairs(string type, Mark start, Mark end, Node[] nodes)
foreach(ref node; nodes) foreach(ref node; nodes)
{ {
enforce(node.isMapping && node.length == 1, enforce(node.isMapping && node.length == 1,
new ConstructorException("While constructing " ~ type ~ new Error("While constructing " ~ type ~
", expected a mapping with single element,", start, ", expected a mapping with single element", start, end));
end));
pairs ~= node.get!(Node.Pair[]); pairs ~= node.get!(Node.Pair[]);
} }
@ -542,8 +539,7 @@ Node.Pair[] constructOrderedMap(Mark start, Mark end, ref Node node)
foreach(ref pair; pairs) foreach(ref pair; pairs)
{ {
enforce((pair.key in map) is null, enforce((pair.key in map) is null,
new ConstructorException("Found a duplicate entry in an ordered map", new Error("Duplicate entry in an ordered map", start, end));
start, end));
map[pair.key] = true; map[pair.key] = true;
} }
clear(map); clear(map);
@ -611,7 +607,7 @@ Node[] constructSet(Mark start, Mark end, ref Node node)
foreach(ref pair; pairs) foreach(ref pair; pairs)
{ {
enforce((pair.key in map) is null, enforce((pair.key in map) is null,
new ConstructorException("Found a duplicate entry in a set", start, end)); new Error("Duplicate entry in a set", start, end));
map[pair.key] = 0; map[pair.key] = 0;
nodes ~= pair.key; nodes ~= pair.key;
} }
@ -679,7 +675,7 @@ Node.Pair[] constructMap(Mark start, Mark end, ref Node node)
foreach(ref pair; pairs) foreach(ref pair; pairs)
{ {
enforce((pair.key in map) is null, enforce((pair.key in map) is null,
new ConstructorException("Found a duplicate entry in a map", start, end)); new Error("Duplicate entry in a map", start, end));
map[pair.key] = true; map[pair.key] = true;
} }
return pairs; return pairs;

View file

@ -140,6 +140,9 @@ struct Dumper
///Always write document end? ///Always write document end?
bool explicitEnd_ = false; bool explicitEnd_ = false;
///Name of the output file or stream, used in error messages.
string name_ = "<unknown>";
public: public:
@disable this(); @disable this();
@ -152,10 +155,12 @@ struct Dumper
*/ */
this(string filename) this(string filename)
{ {
name_ = filename;
try{this(new File(filename, FileMode.OutNew));} try{this(new File(filename, FileMode.OutNew));}
catch(StreamException e) catch(StreamException e)
{ {
throw new YAMLException("Unable to use file for YAML dumping " ~ filename ~ " : " ~ e.msg); throw new YAMLException("Unable to open file " ~ filename ~
" for YAML dumping: " ~ e.msg);
} }
} }
@ -177,6 +182,12 @@ struct Dumper
YAMLVersion_ = null; YAMLVersion_ = null;
} }
///Set stream _name. Used in debugging messages.
@property void name(string name)
{
name_ = name;
}
///Specify custom Resolver to use. ///Specify custom Resolver to use.
void resolver(Resolver resolver) void resolver(Resolver resolver)
{ {
@ -312,7 +323,8 @@ struct Dumper
} }
catch(YAMLException e) catch(YAMLException e)
{ {
throw new YAMLException("Unable to dump YAML: " ~ e.msg); throw new YAMLException("Unable to dump YAML to stream "
~ name_ ~ " : " ~ e.msg);
} }
} }
@ -336,7 +348,8 @@ struct Dumper
} }
catch(YAMLException e) catch(YAMLException e)
{ {
throw new YAMLException("Unable to emit YAML: " ~ e.msg); throw new YAMLException("Unable to emit YAML to stream "
~ name_ ~ " : " ~ e.msg);
} }
} }
} }

View file

@ -47,6 +47,8 @@ class EmitterException : YAMLException
mixin ExceptionCtors; mixin ExceptionCtors;
} }
private alias EmitterException Error;
//Stores results of analysis of a scalar, determining e.g. what scalar style to use. //Stores results of analysis of a scalar, determining e.g. what scalar style to use.
align(4) struct ScalarAnalysis align(4) struct ScalarAnalysis
{ {
@ -230,7 +232,7 @@ struct Emitter
} }
catch(WriteException e) catch(WriteException e)
{ {
throw new EmitterException("Unable to write to stream"); throw new Error("Unable to write to stream: " ~ e.msg);
} }
} }
@ -293,7 +295,7 @@ struct Emitter
bool eventTypeIs(in EventID id) const bool eventTypeIs(in EventID id) const
{ {
enforce(!event_.isNull, enforce(!event_.isNull,
new EmitterException("Expected an event, but no event is available.")); new Error("Expected an event, but no event is available."));
return event_.id == id; return event_.id == id;
} }
@ -307,8 +309,7 @@ struct Emitter
void expectStreamStart() void expectStreamStart()
{ {
enforce(eventTypeIs(EventID.StreamStart), enforce(eventTypeIs(EventID.StreamStart),
new EmitterException("Expected StreamStart, but got " new Error("Expected StreamStart, but got " ~ event_.idString));
~ to!string(event_.id)));
encoding_ = event_.encoding; encoding_ = event_.encoding;
writeStreamStart(); writeStreamStart();
@ -318,7 +319,7 @@ struct Emitter
///Expect nothing, throwing if we still have something. ///Expect nothing, throwing if we still have something.
void expectNothing() void expectNothing()
{ {
throw new EmitterException("Expected nothing, but got " ~ to!string(event_.id)); throw new Error("Expected nothing, but got " ~ event_.idString);
} }
//Document handlers. //Document handlers.
@ -327,8 +328,8 @@ struct Emitter
void expectDocumentStart(bool first)() void expectDocumentStart(bool first)()
{ {
enforce(eventTypeIs(EventID.DocumentStart) || eventTypeIs(EventID.StreamEnd), enforce(eventTypeIs(EventID.DocumentStart) || eventTypeIs(EventID.StreamEnd),
new EmitterException("Expected DocumentStart or StreamEnd, but got " new Error("Expected DocumentStart or StreamEnd, but got "
~ to!string(event_.id))); ~ event_.idString));
if(event_.id == EventID.DocumentStart) if(event_.id == EventID.DocumentStart)
{ {
@ -386,8 +387,7 @@ struct Emitter
void expectDocumentEnd() void expectDocumentEnd()
{ {
enforce(eventTypeIs(EventID.DocumentEnd), enforce(eventTypeIs(EventID.DocumentEnd),
new EmitterException("Expected DocumentEnd, but got " new Error("Expected DocumentEnd, but got " ~ event_.idString));
~ to!string(event_.id)));
writeIndent(); writeIndent();
if(event_.explicitDocument) if(event_.explicitDocument)
@ -450,17 +450,15 @@ struct Emitter
} }
break; break;
default: default:
throw new EmitterException("Expected Alias, Scalar, SequenceStart " throw new Error("Expected Alias, Scalar, SequenceStart or "
"or MappingStart, but got: " "MappingStart, but got: " ~ event_.idString);
~ to!string(event_.id));
} }
} }
///Handle an alias. ///Handle an alias.
void expectAlias() void expectAlias()
{ {
enforce(!event_.anchor.isNull(), enforce(!event_.anchor.isNull(), new Error("Anchor is not specified for alias"));
new EmitterException("Anchor is not specified for alias"));
processAnchor("*"); processAnchor("*");
state_ = popState(); state_ = popState();
} }
@ -785,7 +783,7 @@ struct Emitter
return; return;
} }
enforce(!tag.isNull(), new EmitterException("Tag is not specified")); enforce(!tag.isNull(), new Error("Tag is not specified"));
if(preparedTag_ is null){preparedTag_ = prepareTag(tag);} if(preparedTag_ is null){preparedTag_ = prepareTag(tag);}
if(preparedTag_ !is null && preparedTag_ != "") if(preparedTag_ !is null && preparedTag_ != "")
{ {
@ -841,7 +839,7 @@ struct Emitter
static string prepareVersion(in string YAMLVersion) static string prepareVersion(in string YAMLVersion)
{ {
enforce(YAMLVersion.split(".")[0] == "1", enforce(YAMLVersion.split(".")[0] == "1",
new EmitterException("Unsupported YAML version: " ~ YAMLVersion)); new Error("Unsupported YAML version: " ~ YAMLVersion));
return YAMLVersion; return YAMLVersion;
} }
@ -861,12 +859,12 @@ struct Emitter
static string prepareTagHandle(in string handle) static string prepareTagHandle(in string handle)
{ {
enforce(handle !is null && handle != "", enforce(handle !is null && handle != "",
new EmitterException("Tag handle must not be empty")); new Error("Tag handle must not be empty"));
if(handle.length > 1) foreach(const dchar c; handle[1 .. $ - 1]) if(handle.length > 1) foreach(const dchar c; handle[1 .. $ - 1])
{ {
enforce(isAlphaNum(c) || "-_".canFind(c), enforce(isAlphaNum(c) || "-_".canFind(c),
new EmitterException("Invalid character: " ~ to!string(c) ~ new Error("Invalid character: " ~ to!string(c) ~
" in tag handle " ~ handle)); " in tag handle " ~ handle));
} }
return handle; return handle;
@ -876,7 +874,7 @@ struct Emitter
static string prepareTagPrefix(in string prefix) static string prepareTagPrefix(in string prefix)
{ {
enforce(prefix !is null && prefix != "", enforce(prefix !is null && prefix != "",
new EmitterException("Tag prefix must not be empty")); new Error("Tag prefix must not be empty"));
auto appender = appender!string(); auto appender = appender!string();
const offset = prefix[0] == '!' ? 1 : 0; const offset = prefix[0] == '!' ? 1 : 0;
@ -906,7 +904,7 @@ struct Emitter
///Prepare tag for output. ///Prepare tag for output.
string prepareTag(in Tag tag) string prepareTag(in Tag tag)
{ {
enforce(!tag.isNull(), new EmitterException("Tag must not be empty")); enforce(!tag.isNull(), new Error("Tag must not be empty"));
string tagString = tag.get; string tagString = tag.get;
if(tagString == "!"){return tagString;} if(tagString == "!"){return tagString;}
@ -954,13 +952,12 @@ struct Emitter
static string prepareAnchor(in Anchor anchor) static string prepareAnchor(in Anchor anchor)
{ {
enforce(!anchor.isNull() && anchor.get != "", enforce(!anchor.isNull() && anchor.get != "",
new EmitterException("Anchor must not be empty")); new Error("Anchor must not be empty"));
const str = anchor.get; const str = anchor.get;
foreach(const dchar c; str) foreach(const dchar c; str)
{ {
enforce(isAlphaNum(c) || "-_".canFind(c), enforce(isAlphaNum(c) || "-_".canFind(c),
new EmitterException("Invalid character: " ~ to!string(c) ~ new Error("Invalid character: " ~ to!string(c) ~ " in anchor: " ~ str));
" in anchor: " ~ str));
} }
return str; return str;
} }
@ -1150,8 +1147,7 @@ struct Emitter
break; break;
} }
enforce(stream_.write(bom) == bom.length, enforce(stream_.write(bom) == bom.length, new Error("Unable to write to stream"));
new EmitterException("Unable to write to stream"));
} }
///End the YAML stream. ///End the YAML stream.

View file

@ -83,6 +83,9 @@ struct Event
{ {
return id == EventID.Invalid; return id == EventID.Invalid;
} }
///Get string representation of the token ID.
@property string idString() const {return to!string(id);}
} }
/** /**

View file

@ -16,19 +16,11 @@ import std.string;
///Base class for all exceptions thrown by D:YAML. ///Base class for all exceptions thrown by D:YAML.
class YAMLException : Exception class YAMLException : Exception
{ {
public: ///Construct a YAMLException with specified message and position where it was thrown.
///Construct a YAMLException with specified message, and position where it was thrown. public this(string msg, string file = __FILE__, int line = __LINE__)
this(string msg, string file = __FILE__, int line = __LINE__)
{ {
super(msg, file, line); super(msg, file, line);
} }
package:
//Set name of the file that was being processed when this exception was thrown.
@property name(in string name)
{
msg = name ~ ":\n" ~ msg;
}
} }
///Position in a YAML stream, used for error messages. ///Position in a YAML stream, used for error messages.
@ -65,9 +57,9 @@ abstract class MarkedYAMLException : YAMLException
this(string context, Mark contextMark, string problem, Mark problemMark, this(string context, Mark contextMark, string problem, Mark problemMark,
string file = __FILE__, int line = __LINE__) string file = __FILE__, int line = __LINE__)
{ {
string msg = context ~ '\n'; const msg = context ~ '\n' ~
if(contextMark != problemMark){msg ~= contextMark.toString() ~ '\n';} (contextMark != problemMark ? contextMark.toString() ~ '\n' : "") ~
msg ~= problem ~ '\n' ~ problemMark.toString() ~ '\n'; problem ~ '\n' ~ problemMark.toString() ~ '\n';
super(msg, file, line); super(msg, file, line);
} }
@ -81,8 +73,7 @@ abstract class MarkedYAMLException : YAMLException
//Constructors of YAML exceptions are mostly the same, so we use a mixin. //Constructors of YAML exceptions are mostly the same, so we use a mixin.
template ExceptionCtors() template ExceptionCtors()
{ {
public: public this(string msg, string file = __FILE__, int line = __LINE__)
this(string msg, string file = __FILE__, int line = __LINE__)
{ {
super(msg, file, line); super(msg, file, line);
} }

View file

@ -104,23 +104,23 @@ struct Loader
string name_ = "<unknown>"; string name_ = "<unknown>";
public: public:
@disable this();
/** /**
* Construct a Loader to load YAML from a file. * Construct a Loader to load YAML from a file.
* *
* Params: filename = Name of the file to load from. * Params: filename = Name of the file to load from.
* *
* Throws: YAMLException if the file could not be opened or read from. * Throws: YAMLException if the file could not be opened or read.
*/ */
this(in string filename) this(in string filename)
{ {
try name_ = filename;
{ try{this(new File(filename));}
name = filename;
this(new File(filename));
}
catch(StreamException e) catch(StreamException e)
{ {
throw new YAMLException("Unable to load YAML file " ~ filename ~ " : " ~ e.msg); throw new YAMLException("Unable to open file " ~ filename ~
" for YAML loading: " ~ e.msg);
} }
} }
@ -129,7 +129,7 @@ struct Loader
* *
* Params: stream = Stream to read from. Must be readable. * Params: stream = Stream to read from. Must be readable.
* *
* Throws: YAMLException if stream could not be read from. * Throws: YAMLException if stream could not be read.
*/ */
this(Stream stream) this(Stream stream)
{ {
@ -145,8 +145,8 @@ struct Loader
} }
catch(YAMLException e) catch(YAMLException e)
{ {
e.name = name_; throw new YAMLException("Unable to open stream " ~ name_ ~
throw e; " for YAML loading: " ~ e.msg);
} }
} }
@ -181,7 +181,7 @@ struct Loader
/** /**
* Load single YAML document. * Load single YAML document.
* *
* If none or more than one YAML document is found, this will throw a YAMLException. * If none or more than one YAML document is found, this throws a YAMLException.
* *
* Returns: Root node of the document. * Returns: Root node of the document.
* *
@ -198,8 +198,8 @@ struct Loader
} }
catch(YAMLException e) catch(YAMLException e)
{ {
e.name = name_; throw new YAMLException("Unable to load YAML from stream " ~
throw e; name_ ~ " : " ~ e.msg);
} }
} }
@ -208,15 +208,12 @@ struct Loader
* *
* Returns: Array of root nodes of all documents in the file/stream. * Returns: Array of root nodes of all documents in the file/stream.
* *
* Throws: YAMLException on a YAML parsing error. * Throws: YAMLException on a parsing error.
*/ */
Node[] loadAll() Node[] loadAll()
{ {
Node[] nodes; Node[] nodes;
foreach(ref node; this) foreach(ref node; this){nodes ~= node;}
{
nodes ~= node;
}
return nodes; return nodes;
} }
@ -245,8 +242,8 @@ struct Loader
} }
catch(YAMLException e) catch(YAMLException e)
{ {
e.name = name_; throw new YAMLException("Unable to load YAML from stream " ~
throw e; name_ ~ " : " ~ e.msg);
} }
} }
@ -262,8 +259,8 @@ struct Loader
} }
catch(YAMLException e) catch(YAMLException e)
{ {
e.name = name_; throw new YAMLException("Unable to scan YAML from stream " ~
throw e; name_ ~ " : " ~ e.msg);
} }
} }
@ -278,8 +275,8 @@ struct Loader
} }
catch(YAMLException e) catch(YAMLException e)
{ {
e.name = name_; throw new YAMLException("Unable to parse YAML from stream " ~
throw e; name_ ~ " : " ~ e.msg);
} }
} }
} }

View file

@ -39,10 +39,12 @@ class NodeException : YAMLException
*/ */
this(string msg, Mark start, string file = __FILE__, int line = __LINE__) this(string msg, Mark start, string file = __FILE__, int line = __LINE__)
{ {
super(msg ~ "\nNode at:" ~ start.toString(), file, line); super(msg ~ "\nNode at: " ~ start.toString(), file, line);
} }
} }
private alias NodeException Error;
//Node kinds. //Node kinds.
package enum NodeID : ubyte package enum NodeID : ubyte
{ {
@ -532,8 +534,8 @@ struct Node
void throwUnexpectedType() void throwUnexpectedType()
{ {
//Can't get the value. //Can't get the value.
throw new NodeException("Node has unexpected type " ~ type.toString ~ throw new Error("Node has unexpected type: " ~ type.toString ~
". Expected " ~ typeid(T).toString, startMark_); ". Expected: " ~ typeid(T).toString, startMark_);
} }
static if(isSomeString!T) static if(isSomeString!T)
@ -546,8 +548,7 @@ struct Node
} }
catch(VariantException e) catch(VariantException e)
{ {
throw new NodeException("Unable to convert node value to a string", throw new Error("Unable to convert node value to string", startMark_);
startMark_);
} }
} }
else static if(isFloatingPoint!T) else static if(isFloatingPoint!T)
@ -571,9 +572,9 @@ struct Node
long temp = value_.get!long; long temp = value_.get!long;
if(temp < T.min || temp > T.max) if(temp < T.min || temp > T.max)
{ {
throw new NodeException("Integer value out of range of type " ~ throw new Error("Integer value out of range of type " ~
typeid(T).toString ~ "Value: " ~ typeid(T).toString ~ "Value: " ~ to!string(temp),
to!string(temp), startMark_); startMark_);
} }
target = to!T(temp); target = to!T(temp);
return; return;
@ -602,8 +603,7 @@ struct Node
{ {
if(isSequence) {return get!(Node[]).length;} if(isSequence) {return get!(Node[]).length;}
else if(isMapping){return get!(Pair[]).length;} else if(isMapping){return get!(Pair[]).length;}
throw new NodeException("Trying to get length of a node that is not a collection", throw new Error("Trying to get length of a scalar node", startMark_);
startMark_);
} }
/** /**
@ -637,12 +637,10 @@ struct Node
auto idx = findPair(index); auto idx = findPair(index);
if(idx >= 0){return get!(Pair[])[idx].value;} if(idx >= 0){return get!(Pair[])[idx].value;}
throw new NodeException("Mapping index not found" ~ throw new Error("Mapping index not found" ~
isSomeString!T ? ": " ~ to!string(index) : "", isSomeString!T ? ": " ~ to!string(index) : "", startMark_);
startMark_);
} }
throw new NodeException("Trying to index node that does not support indexing", throw new Error("Trying to index node that does not support indexing", startMark_);
startMark_);
} }
unittest unittest
{ {
@ -723,8 +721,7 @@ struct Node
return; return;
} }
throw new NodeException("Trying to index a YAML node that is not a collection.", throw new Error("Trying to index a scalar node.", startMark_);
startMark_);
} }
unittest unittest
{ {
@ -758,7 +755,7 @@ struct Node
int opApply(T)(int delegate(ref T) dg) int opApply(T)(int delegate(ref T) dg)
{ {
enforce(isSequence, enforce(isSequence,
new NodeException("Trying to iterate over a node that is not a sequence", new Error("Trying to iterate over a node that is not a sequence",
startMark_)); startMark_));
int result = 0; int result = 0;
@ -815,7 +812,7 @@ struct Node
int opApply(K, V)(int delegate(ref K, ref V) dg) int opApply(K, V)(int delegate(ref K, ref V) dg)
{ {
enforce(isMapping, enforce(isMapping,
new NodeException("Trying to iterate over a node that is not a mapping", new Error("Trying to iterate over a node that is not a mapping",
startMark_)); startMark_));
int result = 0; int result = 0;
@ -915,8 +912,7 @@ struct Node
void add(T)(T value) void add(T)(T value)
{ {
enforce(isSequence(), enforce(isSequence(),
new NodeException("Trying to add an element to a " new Error("Trying to add an element to a non-sequence node", startMark_));
"non-sequence YAML node", startMark_));
auto nodes = get!(Node[])(); auto nodes = get!(Node[])();
static if(is(T == Node)){nodes ~= value;} static if(is(T == Node)){nodes ~= value;}
@ -953,8 +949,8 @@ struct Node
void add(K, V)(K key, V value) void add(K, V)(K key, V value)
{ {
enforce(isMapping(), enforce(isMapping(),
new NodeException("Trying to add a key-value pair to a " new Error("Trying to add a key-value pair to a non-mapping node",
"non-mapping YAML node", startMark_)); startMark_));
auto pairs = get!(Node.Pair[])(); auto pairs = get!(Node.Pair[])();
pairs ~= Pair(key, value); pairs ~= Pair(key, value);
@ -1010,8 +1006,7 @@ struct Node
} }
return; return;
} }
throw new NodeException("Trying to remove an element from a YAML node that " throw new Error("Trying to remove an element from a scalar node", startMark_);
"is not a collection.", startMark_);
} }
unittest unittest
{ {
@ -1077,8 +1072,7 @@ struct Node
} }
return; return;
} }
throw new NodeException("Trying to remove an element from a YAML node that " throw new Error("Trying to remove an element from a scalar node", startMark_);
"is not a collection.", startMark_);
} }
unittest unittest
{ {
@ -1334,14 +1328,13 @@ struct Node
{ {
static if(!isIntegral!T) static if(!isIntegral!T)
{ {
throw new NodeException("Indexing a YAML sequence with a non-integral type.", throw new Error("Indexing a sequence with a non-integral type.", startMark_);
startMark_);
} }
else else
{ {
enforce(index >= 0 && index < value_.get!(Node[]).length, enforce(index >= 0 && index < value_.get!(Node[]).length,
new NodeException("Index to a YAML sequence out of range: " new Error("Sequence index out of range: " ~ to!string(index),
~ to!string(index), startMark_)); startMark_));
} }
} }
} }

View file

@ -99,6 +99,8 @@ class ParserException : MarkedYAMLException
mixin MarkedExceptionCtors; mixin MarkedExceptionCtors;
} }
private alias ParserException Error;
///Generates events from tokens provided by a Scanner. ///Generates events from tokens provided by a Scanner.
final class Parser final class Parser
{ {
@ -225,7 +227,7 @@ final class Parser
Event delegate() popState() Event delegate() popState()
{ {
enforce(states_.length > 0, enforce(states_.length > 0,
new YAMLException("Parser: Need to pop a state but there are no states left")); new YAMLException("Parser: Need to pop state but no states left to pop"));
const result = states_.back(); const result = states_.back();
states_.popBack; states_.popBack;
return result; return result;
@ -235,7 +237,7 @@ final class Parser
Mark popMark() Mark popMark()
{ {
enforce(marks_.length > 0, enforce(marks_.length > 0,
new YAMLException("Parser: Need to pop a mark but there are no marks left")); new YAMLException("Parser: Need to pop mark but no marks left to pop"));
const result = marks_.back(); const result = marks_.back();
marks_.popBack; marks_.popBack;
return result; return result;
@ -286,8 +288,8 @@ final class Parser
auto tagDirectives = processDirectives(); auto tagDirectives = processDirectives();
enforce(scanner_.checkToken(TokenID.DocumentStart), enforce(scanner_.checkToken(TokenID.DocumentStart),
new ParserException("Expected document start but found " ~ new Error("Expected document start but found " ~
to!string(scanner_.peekToken().id), scanner_.peekToken().idString,
scanner_.peekToken().startMark)); scanner_.peekToken().startMark));
const endMark = scanner_.getToken().endMark; const endMark = scanner_.getToken().endMark;
@ -347,12 +349,11 @@ final class Parser
if(name == "YAML") if(name == "YAML")
{ {
enforce(YAMLVersion_ is null, enforce(YAMLVersion_ is null,
new ParserException("Found duplicate YAML directive", new Error("Duplicate YAML directive", token.startMark));
token.startMark));
const minor = parts[1].split(".")[0]; const minor = parts[1].split(".")[0];
enforce(to!int(minor) == 1, enforce(to!int(minor) == 1,
new ParserException("Found incompatible YAML document (version " new Error("Incompatible document (version 1.x is required)",
"1.* is required)", token.startMark)); token.startMark));
YAMLVersion_ = parts[1]; YAMLVersion_ = parts[1];
} }
else if(name == "TAG") else if(name == "TAG")
@ -365,8 +366,8 @@ final class Parser
//handle //handle
auto h = pair[0]; auto h = pair[0];
auto replacement = pair[1]; auto replacement = pair[1];
enforce(h != handle, new ParserException("Duplicate tag handle: " ~ enforce(h != handle, new Error("Duplicate tag handle: " ~ handle,
handle, token.startMark)); token.startMark));
} }
tagHandles_ ~= tuple(handle, parts[2]); tagHandles_ ~= tuple(handle, parts[2]);
} }
@ -515,9 +516,9 @@ final class Parser
} }
Token token = scanner_.peekToken(); Token token = scanner_.peekToken();
throw new ParserException("While parsing a " ~ (block ? "block" : "flow") ~ " node", throw new Error("While parsing a " ~ (block ? "block" : "flow") ~ " node",
startMark, "expected the node content, but found: " startMark, "expected the node content, but found: "
~ to!string(token.id), token.startMark); ~ token.idString, token.startMark);
} }
/** /**
@ -549,7 +550,7 @@ final class Parser
} }
//handle must be in tagHandles_ //handle must be in tagHandles_
enforce(replacement !is null, enforce(replacement !is null,
new ParserException("While parsing a node", startMark, new Error("While parsing a node", startMark,
"found undefined tag handle: " ~ handle, tagMark)); "found undefined tag handle: " ~ handle, tagMark));
return replacement ~ suffix; return replacement ~ suffix;
} }
@ -584,9 +585,9 @@ final class Parser
if(!scanner_.checkToken(TokenID.BlockEnd)) if(!scanner_.checkToken(TokenID.BlockEnd))
{ {
Token token = scanner_.peekToken(); Token token = scanner_.peekToken();
throw new ParserException("While parsing a block collection", marks_[$ - 1], throw new Error("While parsing a block collection", marks_[$ - 1],
"expected block end, but found " "expected block end, but found " ~ token.idString,
~ to!string(token.id), token.startMark); token.startMark);
} }
state_ = popState(); state_ = popState();
@ -649,9 +650,9 @@ final class Parser
if(!scanner_.checkToken(TokenID.BlockEnd)) if(!scanner_.checkToken(TokenID.BlockEnd))
{ {
Token token = scanner_.peekToken(); Token token = scanner_.peekToken();
throw new ParserException("While parsing a block mapping", marks_[$ - 1], throw new Error("While parsing a block mapping", marks_[$ - 1],
"expected block end, but found: " "expected block end, but found: " ~ token.idString,
~ to!string(token.id), token.startMark); token.startMark);
} }
state_ = popState(); state_ = popState();
@ -710,10 +711,9 @@ final class Parser
else else
{ {
Token token = scanner_.peekToken; Token token = scanner_.peekToken;
throw new ParserException("While parsing a flow sequence", throw new Error("While parsing a flow sequence", marks_[$ - 1],
marks_[$ - 1],
"expected ',' or ']', but got: " ~ "expected ',' or ']', but got: " ~
to!string(token.id), token.startMark); token.idString, token.startMark);
} }
} }
@ -818,10 +818,9 @@ final class Parser
else else
{ {
Token token = scanner_.peekToken; Token token = scanner_.peekToken;
throw new ParserException("While parsing a flow mapping", throw new Error("While parsing a flow mapping", marks_[$ - 1],
marks_[$ - 1],
"expected ',' or '}', but got: " ~ "expected ',' or '}', but got: " ~
to!string(token.id), token.startMark); token.idString, token.startMark);
} }
} }

View file

@ -29,7 +29,7 @@ class ReaderException : YAMLException
{ {
this(string msg, string file = __FILE__, int line = __LINE__) this(string msg, string file = __FILE__, int line = __LINE__)
{ {
super("Error reading YAML stream: " ~ msg, file, line); super("Error reading stream: " ~ msg, file, line);
} }
} }
@ -105,11 +105,11 @@ final class Reader
rawBuffer16_[0] = stream_.getcw(); rawBuffer16_[0] = stream_.getcw();
rawUsed_ = 1; rawUsed_ = 1;
enforce(stream_.available % 2 == 0, enforce(stream_.available % 2 == 0,
new ReaderException("Odd number of bytes in an UTF-16 stream")); new ReaderException("Odd byte count in an UTF-16 stream"));
break; break;
case 3, 4: case 3, 4:
enforce(stream_.available % 4 == 0, enforce(stream_.available % 4 == 0,
new ReaderException("Number of bytes in an UTF-32 stream not divisible by 4")); new ReaderException("Byte count in an UTF-32 stream not divisible by 4"));
encoding_ = Encoding.UTF_32; encoding_ = Encoding.UTF_32;
break; break;
default: assert(false, "Unknown UTF BOM"); default: assert(false, "Unknown UTF BOM");
@ -286,7 +286,7 @@ final class Reader
* *
* Params: chars = Maximum number of characters to load. * Params: chars = Maximum number of characters to load.
* *
* Throws: ReaderException on unicode decoding error, * Throws: ReaderException on Unicode decoding error,
* if nonprintable characters are detected, or * if nonprintable characters are detected, or
* if there is an error reading from the stream. * if there is an error reading from the stream.
*/ */
@ -382,13 +382,12 @@ final class Reader
catch(UtfException e) catch(UtfException e)
{ {
const position = stream_.position; const position = stream_.position;
throw new ReaderException("Unicode decoding error between bytes " ~ throw new ReaderException(format("Unicode decoding error between bytes ",
to!string(oldPosition) ~ " and " ~ oldPosition, " and ", position, " : ", e.msg));
to!string(position) ~ " " ~ e.msg);
} }
catch(ReadException e) catch(ReadException e)
{ {
throw new ReaderException("Error reading from the stream: " ~ e.msg); throw new ReaderException(e.msg);
} }
enforce(printable(buffer_[oldLength .. $]), enforce(printable(buffer_[oldLength .. $]),

View file

@ -73,12 +73,12 @@ final class Representer
/** /**
* Add a function to represent nodes with a specific data type. * Add a function to represent nodes with a specific data type.
* *
* The representer function takes a reference to a Node storing the data * The representer function takes references to a Node storing the data
* type and to the Representer. It returns the represented node and may * type and to the Representer. It returns the represented node and may
* throw a RepresenterException. See the example for more information. * throw a RepresenterException. See the example for more information.
* *
* Only one function may be specified for one data type. Default data * Only one function may be specified for one data type. Default data
* types already have representer functions unless disabled in these * types already have representer functions unless disabled in the
* Representer constructor. * Representer constructor.
* *
* Params: representer = Representer function to add. * Params: representer = Representer function to add.
@ -297,8 +297,8 @@ final class Representer
auto type = data.isUserType ? data.get!YAMLObject.type : data.type; auto type = data.isUserType ? data.get!YAMLObject.type : data.type;
enforce((type in representers_) !is null, enforce((type in representers_) !is null,
new RepresenterException("No YAML representer function for type " new RepresenterException("No representer function for type "
~ type.toString() ~ " cannot represent.")); ~ type.toString() ~ " , cannot represent."));
Node result = representers_[type](data, this); Node result = representers_[type](data, this);
if(!data.tag.isNull()){result.tag = data.tag;} if(!data.tag.isNull()){result.tag = data.tag;}
return result; return result;
@ -426,8 +426,7 @@ Node representPairs(ref Node node, Representer representer)
if(node.tag == Tag("tag:yaml.org,2002:omap")) if(node.tag == Tag("tag:yaml.org,2002:omap"))
{ {
enforce(!hasDuplicates(pairs), enforce(!hasDuplicates(pairs),
new RepresenterException("Found a duplicate entry " new RepresenterException("Duplicate entry in an ordered map"));
"in an ordered map"));
return representer.representSequence(node.tag.get, mapToSequence(pairs)); return representer.representSequence(node.tag.get, mapToSequence(pairs));
} }
else if(node.tag == Tag("tag:yaml.org,2002:pairs")) else if(node.tag == Tag("tag:yaml.org,2002:pairs"))
@ -437,8 +436,7 @@ Node representPairs(ref Node node, Representer representer)
else else
{ {
enforce(!hasDuplicates(pairs), enforce(!hasDuplicates(pairs),
new RepresenterException("Found a duplicate entry " new RepresenterException("Duplicate entry in an unordered map"));
"in an unordered map"));
return representer.representMapping("tag:yaml.org,2002:map", pairs); return representer.representMapping("tag:yaml.org,2002:map", pairs);
} }
} }

View file

@ -63,6 +63,8 @@ class ScannerException : MarkedYAMLException
mixin MarkedExceptionCtors; mixin MarkedExceptionCtors;
} }
private alias ScannerException Error;
///Generates tokens from data provided by a Reader. ///Generates tokens from data provided by a Reader.
final class Scanner final class Scanner
{ {
@ -269,9 +271,9 @@ final class Scanner
if(c == '\"') {return fetchDouble();} if(c == '\"') {return fetchDouble();}
if(checkPlain()) {return fetchPlain();} if(checkPlain()) {return fetchPlain();}
throw new ScannerException(format("While scanning for the next token, found " throw new Error(format("While scanning for the next token, found "
"character \'", c, "\', index ",to!int(c), "character \'", c, "\', index ",to!int(c),
" that cannot start " "any token"), reader_.mark); " that cannot start any token"), reader_.mark);
} }
@ -303,7 +305,7 @@ final class Scanner
if(key.line != reader_.line || reader_.charIndex - key.charIndex > 1024) if(key.line != reader_.line || reader_.charIndex - key.charIndex > 1024)
{ {
enforce(!key.required, enforce(!key.required,
new ScannerException("While scanning a simple key", new Error("While scanning a simple key",
Mark(key.line, key.column), Mark(key.line, key.column),
"could not find expected ':'", reader_.mark)); "could not find expected ':'", reader_.mark));
levelsToRemove ~= level; levelsToRemove ~= level;
@ -341,10 +343,8 @@ final class Scanner
{ {
auto key = possibleSimpleKeys_[flowLevel_]; auto key = possibleSimpleKeys_[flowLevel_];
enforce(!key.required, enforce(!key.required,
new ScannerException("While scanning a simple key", new Error("While scanning a simple key", Mark(key.line, key.column),
Mark(key.line, key.column), "could not find expected ':'", reader_.mark));
"could not find expected ':'",
reader_.mark));
possibleSimpleKeys_.remove(flowLevel_); possibleSimpleKeys_.remove(flowLevel_);
} }
} }
@ -369,7 +369,7 @@ final class Scanner
//restrictive than what the specification requires. //restrictive than what the specification requires.
//if(pedantic_ && flowLevel_ > 0 && indent_ > column) //if(pedantic_ && flowLevel_ > 0 && indent_ > column)
//{ //{
// throw new ScannerException("Invalid intendation or unclosed '[' or '{'", // throw new Error("Invalid intendation or unclosed '[' or '{'",
// reader_.mark) // reader_.mark)
//} //}
return; return;
@ -510,7 +510,7 @@ final class Scanner
void blockChecks(string type, TokenID id)() void blockChecks(string type, TokenID id)()
{ {
//Are we allowed to start a key (not neccesarily a simple one)? //Are we allowed to start a key (not neccesarily a simple one)?
enforce(allowSimpleKey_, new ScannerException(type ~ " keys are not allowed here", enforce(allowSimpleKey_, new Error(type ~ " keys are not allowed here",
reader_.mark)); reader_.mark));
if(addIndent(reader_.column)) if(addIndent(reader_.column))
@ -585,8 +585,7 @@ final class Scanner
{ {
//We can start a complex value if and only if we can start a simple key. //We can start a complex value if and only if we can start a simple key.
enforce(flowLevel_ > 0 || allowSimpleKey_, enforce(flowLevel_ > 0 || allowSimpleKey_,
new ScannerException("Mapping values are not allowed here", new Error("Mapping values are not allowed here", reader_.mark));
reader_.mark));
//If this value starts a new block mapping, we need to add //If this value starts a new block mapping, we need to add
//BLOCK-MAPPING-START. It'll be detected as an error later by the parser. //BLOCK-MAPPING-START. It'll be detected as an error later by the parser.
@ -777,9 +776,9 @@ final class Scanner
} }
enforce(length > 0, enforce(length > 0,
new ScannerException("While scanning " ~ name, startMark, new Error("While scanning " ~ name, startMark,
"expected alphanumeric, - or _, but found " "expected alphanumeric, - or _, but found " ~ to!string(c),
~ to!string(c), reader_.mark)); reader_.mark));
return reader_.get(length); return reader_.get(length);
} }
@ -856,7 +855,7 @@ final class Scanner
const name = scanAlphaNumeric!"a directive"(startMark); const name = scanAlphaNumeric!"a directive"(startMark);
enforce(" \0\n\r\u0085\u2028\u2029".canFind(reader_.peek()), enforce(" \0\n\r\u0085\u2028\u2029".canFind(reader_.peek()),
new ScannerException("While scanning a directive", startMark, new Error("While scanning a directive", startMark,
"expected alphanumeric, - or _, but found " "expected alphanumeric, - or _, but found "
~ to!string(reader_.peek()), reader_.mark)); ~ to!string(reader_.peek()), reader_.mark));
return name; return name;
@ -869,7 +868,7 @@ final class Scanner
dstring result = scanYAMLDirectiveNumber(startMark); dstring result = scanYAMLDirectiveNumber(startMark);
enforce(reader_.peek() == '.', enforce(reader_.peek() == '.',
new ScannerException("While scanning a directive", startMark, new Error("While scanning a directive", startMark,
"expected a digit or '.', but found: " "expected a digit or '.', but found: "
~ to!string(reader_.peek()), reader_.mark)); ~ to!string(reader_.peek()), reader_.mark));
//Skip the '.'. //Skip the '.'.
@ -877,7 +876,7 @@ final class Scanner
result ~= '.' ~ scanYAMLDirectiveNumber(startMark); result ~= '.' ~ scanYAMLDirectiveNumber(startMark);
enforce(" \0\n\r\u0085\u2028\u2029".canFind(reader_.peek()), enforce(" \0\n\r\u0085\u2028\u2029".canFind(reader_.peek()),
new ScannerException("While scanning a directive", startMark, new Error("While scanning a directive", startMark,
"expected a digit or '.', but found: " "expected a digit or '.', but found: "
~ to!string(reader_.peek()), reader_.mark)); ~ to!string(reader_.peek()), reader_.mark));
return result; return result;
@ -887,7 +886,7 @@ final class Scanner
dstring scanYAMLDirectiveNumber(in Mark startMark) dstring scanYAMLDirectiveNumber(in Mark startMark)
{ {
enforce(isDigit(reader_.peek()), enforce(isDigit(reader_.peek()),
new ScannerException("While scanning a directive", startMark, new Error("While scanning a directive", startMark,
"expected a digit, but found: " ~ "expected a digit, but found: " ~
to!string(reader_.peek()), reader_.mark)); to!string(reader_.peek()), reader_.mark));
@ -912,9 +911,9 @@ final class Scanner
{ {
const value = scanTagHandle("directive", startMark); const value = scanTagHandle("directive", startMark);
enforce(reader_.peek() == ' ', enforce(reader_.peek() == ' ',
new ScannerException("While scanning a directive handle", startMark, new Error("While scanning a directive handle", startMark,
"expected ' ', but found: " ~ "expected ' ', but found: " ~ to!string(reader_.peek()),
to!string(reader_.peek()), reader_.mark)); reader_.mark));
return value; return value;
} }
@ -923,7 +922,7 @@ final class Scanner
{ {
const value = scanTagURI("directive", startMark); const value = scanTagURI("directive", startMark);
enforce(" \0\n\r\u0085\u2028\u2029".canFind(reader_.peek()), enforce(" \0\n\r\u0085\u2028\u2029".canFind(reader_.peek()),
new ScannerException("While scanning a directive prefix", startMark, new Error("While scanning a directive prefix", startMark,
"expected ' ', but found" ~ to!string(reader_.peek()), "expected ' ', but found" ~ to!string(reader_.peek()),
reader_.mark)); reader_.mark));
@ -936,7 +935,7 @@ final class Scanner
findNextNonSpace(); findNextNonSpace();
if(reader_.peek() == '#'){scanToNextBreak();} if(reader_.peek() == '#'){scanToNextBreak();}
enforce("\0\n\r\u0085\u2028\u2029".canFind(reader_.peek()), enforce("\0\n\r\u0085\u2028\u2029".canFind(reader_.peek()),
new ScannerException("While scanning a directive", startMark, new Error("While scanning a directive", startMark,
"expected comment or a line break, but found" "expected comment or a line break, but found"
~ to!string(reader_.peek()), reader_.mark)); ~ to!string(reader_.peek()), reader_.mark));
scanLineBreak(); scanLineBreak();
@ -966,7 +965,7 @@ final class Scanner
enforce((" \t\0\n\r\u0085\u2028\u2029".canFind(reader_.peek()) || enforce((" \t\0\n\r\u0085\u2028\u2029".canFind(reader_.peek()) ||
("?:,]}%@").canFind(reader_.peek())), ("?:,]}%@").canFind(reader_.peek())),
new ScannerException("While scanning an " ~ (i == '*') ? "alias" : "anchor", new Error("While scanning an " ~ (i == '*') ? "alias" : "anchor",
startMark, "expected alphanumeric, - or _, but found "~ startMark, "expected alphanumeric, - or _, but found "~
to!string(reader_.peek()), reader_.mark)); to!string(reader_.peek()), reader_.mark));
@ -994,9 +993,9 @@ final class Scanner
reader_.forward(2); reader_.forward(2);
suffix = scanTagURI("tag", startMark); suffix = scanTagURI("tag", startMark);
enforce(reader_.peek() == '>', enforce(reader_.peek() == '>',
new ScannerException("While scanning a tag", startMark, new Error("While scanning a tag", startMark,
"expected '>' but found" ~ "expected '>' but found" ~ to!string(reader_.peek()),
to!string(reader_.peek()), reader_.mark)); reader_.mark));
reader_.forward(); reader_.forward();
} }
else if(" \t\0\n\r\u0085\u2028\u2029".canFind(c)) else if(" \t\0\n\r\u0085\u2028\u2029".canFind(c))
@ -1031,9 +1030,9 @@ final class Scanner
} }
enforce(" \0\n\r\u0085\u2028\u2029".canFind(reader_.peek()), enforce(" \0\n\r\u0085\u2028\u2029".canFind(reader_.peek()),
new ScannerException("While scanning a tag", startMark, new Error("While scanning a tag", startMark,
"expected ' ' but found" ~ "expected ' ' but found" ~ to!string(reader_.peek()),
to!string(reader_.peek()), reader_.mark)); reader_.mark));
return tagToken(startMark, reader_.mark, to!string(handle ~ '\0' ~ suffix)); return tagToken(startMark, reader_.mark, to!string(handle ~ '\0' ~ suffix));
} }
@ -1141,9 +1140,9 @@ final class Scanner
if(!isDigit(c)){return false;} if(!isDigit(c)){return false;}
increment = to!int(""d ~ c); increment = to!int(""d ~ c);
enforce(increment != 0, enforce(increment != 0,
new ScannerException("While scanning a block scalar", startMark, new Error("While scanning a block scalar", startMark,
"expected indentation indicator in range 1-9, " "expected indentation indicator in range 1-9, but found 0",
"but found 0", reader_.mark)); reader_.mark));
reader_.forward(); reader_.forward();
c = reader_.peek(); c = reader_.peek();
return true; return true;
@ -1154,9 +1153,9 @@ final class Scanner
else if(getIncrement()){getChomping();} else if(getIncrement()){getChomping();}
enforce(" \0\n\r\u0085\u2028\u2029".canFind(c), enforce(" \0\n\r\u0085\u2028\u2029".canFind(c),
new ScannerException("While scanning a block scalar", startMark, new Error("While scanning a block scalar", startMark,
"expected chomping or indentation indicator, " "expected chomping or indentation indicator, but found "
"but found " ~ to!string(c), reader_.mark)); ~ to!string(c), reader_.mark));
return tuple(chomping, increment); return tuple(chomping, increment);
} }
@ -1168,7 +1167,7 @@ final class Scanner
if(reader_.peek == '#'){scanToNextBreak();} if(reader_.peek == '#'){scanToNextBreak();}
enforce("\0\n\r\u0085\u2028\u2029".canFind(reader_.peek()), enforce("\0\n\r\u0085\u2028\u2029".canFind(reader_.peek()),
new ScannerException("While scanning a block scalar", startMark, new Error("While scanning a block scalar", startMark,
"expected a comment or a line break, but found " "expected a comment or a line break, but found "
~ to!string(reader_.peek()), reader_.mark)); ~ to!string(reader_.peek()), reader_.mark));
scanLineBreak(); scanLineBreak();
@ -1300,7 +1299,7 @@ final class Scanner
foreach(i; 0 .. length) foreach(i; 0 .. length)
{ {
enforce(isHexDigit(reader_.peek(i)), enforce(isHexDigit(reader_.peek(i)),
new ScannerException( new Error(
"While scanning a double qouted scalar", startMark, "While scanning a double qouted scalar", startMark,
"expected escape sequence of " ~ to!string(length) ~ "expected escape sequence of " ~ to!string(length) ~
" hexadecimal numbers, but found " ~ " hexadecimal numbers, but found " ~
@ -1317,7 +1316,7 @@ final class Scanner
} }
else else
{ {
throw new ScannerException("While scanning a double quoted scalar", throw new Error("While scanning a double quoted scalar",
startMark, startMark,
"found unknown escape character: " ~ "found unknown escape character: " ~
to!string(c), reader_.mark); to!string(c), reader_.mark);
@ -1336,7 +1335,7 @@ final class Scanner
dchar c = reader_.peek(); dchar c = reader_.peek();
enforce(c != '\0', enforce(c != '\0',
new ScannerException("While scanning a quoted scalar", startMark, new Error("While scanning a quoted scalar", startMark,
"found unexpected end of stream", reader_.mark)); "found unexpected end of stream", reader_.mark));
auto appender = appender!dstring(); auto appender = appender!dstring();
@ -1364,9 +1363,8 @@ final class Scanner
if((prefix == "---" || prefix == "...") && if((prefix == "---" || prefix == "...") &&
" \t\0\n\r\u0085\u2028\u2029".canFind(reader_.peek(3))) " \t\0\n\r\u0085\u2028\u2029".canFind(reader_.peek(3)))
{ {
throw new ScannerException("While scanning a quoted scalar", startMark, throw new Error("While scanning a quoted scalar", startMark,
"found unexpected document separator", "found unexpected document separator", reader_.mark);
reader_.mark);
} }
while(" \t".canFind(reader_.peek())){reader_.forward();} while(" \t".canFind(reader_.peek())){reader_.forward();}
@ -1417,7 +1415,7 @@ final class Scanner
!",[]{}".canFind(reader_.peek(length + 1))) !",[]{}".canFind(reader_.peek(length + 1)))
{ {
reader_.forward(length); reader_.forward(length);
throw new ScannerException("While scanning a plain scalar", startMark, throw new Error("While scanning a plain scalar", startMark,
"found unexpected ':' . Please check " "found unexpected ':' . Please check "
"http://pyyaml.org/wiki/YAMLColonInFlowContext " "http://pyyaml.org/wiki/YAMLColonInFlowContext "
"for details.", reader_.mark); "for details.", reader_.mark);
@ -1494,9 +1492,8 @@ final class Scanner
{ {
dchar c = reader_.peek(); dchar c = reader_.peek();
enforce(c == '!', enforce(c == '!',
new ScannerException("While scanning a " ~ name, startMark, new Error("While scanning a " ~ name, startMark,
"expected a '!', but found: " ~ to!string(c), "expected a '!', but found: " ~ to!string(c), reader_.mark));
reader_.mark));
uint length = 1; uint length = 1;
c = reader_.peek(length); c = reader_.peek(length);
@ -1510,7 +1507,7 @@ final class Scanner
if(c != '!') if(c != '!')
{ {
reader_.forward(length); reader_.forward(length);
throw new ScannerException("While scanning a " ~ name, startMark, throw new Error("While scanning a " ~ name, startMark,
"expected a '!', but found: " ~ to!string(c), "expected a '!', but found: " ~ to!string(c),
reader_.mark); reader_.mark);
} }
@ -1544,9 +1541,8 @@ final class Scanner
length = 0; length = 0;
} }
enforce(appender.data.length > 0, enforce(appender.data.length > 0,
new ScannerException("While parsing a " ~ name, startMark, new Error("While parsing a " ~ name, startMark,
"expected URI, but found: " ~ to!string(c), "expected URI, but found: " ~ to!string(c), reader_.mark));
reader_.mark));
return appender.data; return appender.data;
} }
@ -1567,8 +1563,8 @@ final class Scanner
foreach(k; 0 .. 2) foreach(k; 0 .. 2)
{ {
dchar c = reader_.peek(k); dchar c = reader_.peek(k);
enforce("0123456789ABCDEFabcdef".canFind(c), enforce(isHexDigit(c),
new ScannerException("While scanning a " ~ name, startMark, new Error("While scanning a " ~ name, startMark,
"expected URI escape sequence of " "expected URI escape sequence of "
"2 hexadecimal numbers, but found: " ~ "2 hexadecimal numbers, but found: " ~
to!string(c), reader_.mark)); to!string(c), reader_.mark));
@ -1589,11 +1585,11 @@ final class Scanner
try{return to!dstring(cast(string)bytes);} try{return to!dstring(cast(string)bytes);}
catch(ConvException e) catch(ConvException e)
{ {
throw new ScannerException("While scanning a " ~ name, startMark, e.msg, mark); throw new Error("While scanning a " ~ name, startMark, e.msg, mark);
} }
catch(UtfException e) catch(UtfException e)
{ {
throw new ScannerException("While scanning a " ~ name, startMark, e.msg, mark); throw new Error("While scanning a " ~ name, startMark, e.msg, mark);
} }
} }

View file

@ -11,6 +11,8 @@
module dyaml.token; module dyaml.token;
import std.conv;
import dyaml.encoding; import dyaml.encoding;
import dyaml.exception; import dyaml.exception;
import dyaml.reader; import dyaml.reader;
@ -81,6 +83,9 @@ immutable struct Token
ScalarStyle style; ScalarStyle style;
///Encoding, if this is a stream start token. ///Encoding, if this is a stream start token.
Encoding encoding; Encoding encoding;
///Get string representation of the token ID.
@property string idString() const {return to!string(id);}
} }
/** /**