Simplified the Constructor API.

This commit is contained in:
Ferdinand Majerech 2011-11-17 23:53:24 +01:00
parent ab154480fb
commit fbc962623d
16 changed files with 217 additions and 297 deletions

Binary file not shown.

View file

@ -3,13 +3,13 @@ Custom YAML data types
====================== ======================
Sometimes you need to serialize complex data types such as classes. To do this Sometimes you need to serialize complex data types such as classes. To do this
you could use plain nodes such as mappings with class data members. However, you could use plain nodes such as mappings with class data members. YAML also
YAML supports custom types with identifiers called *tags*. That is the topic of supports custom types with identifiers called *tags*. That is the topic of this
this tutorial. tutorial.
Each YAML node has a tag specifying its type. For instance: strings use the tag Each YAML node has a tag specifying its type. For instance: strings use the tag
``tag:yaml.org,2002:str``. Tags of most default types are *implicitly resolved* ``tag:yaml.org,2002:str``. Tags of most default types are *implicitly resolved*
during parsing, so you don't need to specify tag for each float, integer, etc. during parsing - you don't need to specify tag for each float, integer, etc.
D:YAML can also implicitly resolve custom tags, as we will show later. D:YAML can also implicitly resolve custom tags, as we will show later.
@ -24,7 +24,7 @@ the *addConstructorXXX()* methods, where *XXX* is *Scalar*, *Sequence* or
*Mapping*. *Constructor* is then passed to *Loader*, which parses YAML input. *Mapping*. *Constructor* is then passed to *Loader*, which parses YAML input.
Struct types have no specific requirements for YAML support. Class types should Struct types have no specific requirements for YAML support. Class types should
define the *opEquals()* operator, as this is used in equality comparisons of define the *opEquals()* operator - this is used in equality comparisons of
nodes. Default class *opEquals()* compares references, which means two identical nodes. Default class *opEquals()* compares references, which means two identical
objects might be considered unequal. (Default struct *opEquals()* compares objects might be considered unequal. (Default struct *opEquals()* compares
byte-by-byte, sometimes you might want to override that as well.) byte-by-byte, sometimes you might want to override that as well.)
@ -41,24 +41,26 @@ following struct:
ubyte blue; ubyte blue;
} }
First, we need a function to construct our data type. It must take two *Mark* First, we need a function to construct our data type. The function will take a
structs, which store position of the node in the file, and a reference to *Node* reference to *Node* to construct from. The node is guaranteed to contain either
to construct from. The node is guaranteed to contain either a *string*, an array a *string*, an array of *Node* or of *Node.Pair*, depending on whether we're
of *Node* or of *Node.Pair*, depending on whether we're constructing our value constructing our value from a scalar, sequence, or mapping, respectively.
from a scalar, sequence, or mapping, respectively. In this tutorial, we have If this function throws any exception, D:YAML handles it and adds its message
functions to construct a color from a scalar, using CSS-like format, RRGGBB, or to a *YAMLException* that will be thrown when loading the file.
from a mapping, where we use the following format: {r:RRR, g:GGG, b:BBB} . Code
of these functions: In this tutorial, we have functions to construct a color from a scalar, using
CSS-like format, RRGGBB, or from a mapping, where we use the following format:
{r:RRR, g:GGG, b:BBB} . Code of these functions:
.. code-block:: d .. code-block:: d
Color constructColorScalar(Mark start, Mark end, ref Node node) Color constructColorScalar(ref Node node)
{ {
string value = node.as!string; string value = node.as!string;
if(value.length != 6) if(value.length != 6)
{ {
throw new ConstructorException("Invalid color: " ~ value, start, end); throw new Exception("Invalid color: " ~ value);
} }
//We don't need to check for uppercase chars this way. //We don't need to check for uppercase chars this way.
value = value.toLower(); value = value.toLower();
@ -68,7 +70,7 @@ of these functions:
{ {
if(!std.ascii.isHexDigit(c)) if(!std.ascii.isHexDigit(c))
{ {
throw new ConstructorException("Invalid color: " ~ value, start, end); throw new Exception("Invalid color: " ~ value);
} }
if(std.ascii.isDigit(c)) if(std.ascii.isDigit(c))
@ -86,21 +88,16 @@ of these functions:
return result; return result;
} }
Color constructColorMapping(Mark start, Mark end, ref Node node) Color constructColorMapping(ref Node node)
{ {
ubyte r,g,b; ubyte r,g,b;
//Might throw if a value is missing is not an integer, or is out of range. //Might throw if a value is missing is not an integer, or is out of range.
try //If this happens, D:YAML will handle the exception and use its message
{ //in a YAMLException thrown when loading.
r = node["r"].as!ubyte; r = node["r"].as!ubyte;
g = node["g"].as!ubyte; g = node["g"].as!ubyte;
b = node["b"].as!ubyte; b = node["b"].as!ubyte;
}
catch(NodeException e)
{
throw new ConstructorException("Invalid color: " ~ e.msg, start, end);
}
return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b); return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b);
} }

View file

@ -95,14 +95,20 @@
</table></div> </table></div>
</dd> </dd>
<dt class="d_decl">void <a name="addConstructorScalar"></a><span class="ddoc_psymbol">addConstructorScalar</span>(T)(in string <b>tag</b>, T function(Mark, Mark, ref Node) <b>ctor</b>); <dt class="d_decl">void <a name="addConstructorScalar"></a><span class="ddoc_psymbol">addConstructorScalar</span>(T)(in string <b>tag</b>, T function(ref Node) <b>ctor</b>);
</dt> </dt>
<dd><p>Add a constructor function from scalar. <dd><p>Add a constructor function from scalar.
</p> </p>
<p>The function must take two Marks (start and end positions of <p>The function must take a reference to Node to construct from.
the node in file) and a reference to Node to construct from.
The node contains a string for scalars, Node[] for sequences and The node contains a string for scalars, Node[] for sequences and
Node.Pair[] for mappings. Node.Pair[] for mappings.
<br>
Any exception thrown by this function will be caught by D:YAML and
its message will be added to a YAMLException that will also tell the
user which type failed to construct, and position in the file.
<br>
The value returned by this function will be stored in the resulting node. The value returned by this function will be stored in the resulting node.
<br> <br>
@ -124,20 +130,13 @@
<span class="d_keyword">int</span> x, y, z; <span class="d_keyword">int</span> x, y, z;
} }
MyStruct constructMyStructScalar(Mark start, Mark end, <span class="d_keyword">ref</span> Node node) MyStruct constructMyStructScalar(<span class="d_keyword">ref</span> Node node)
{ {
<span class="d_comment">//Guaranteed to be string as we construct from scalar. <span class="d_comment">//Guaranteed to be string as we construct from scalar.
</span> <span class="d_comment">//!mystruct x:y:z </span> <span class="d_comment">//!mystruct x:y:z
</span> <span class="d_keyword">auto</span> parts = node.as!string().split(<span class="d_string">":"</span>); </span> <span class="d_keyword">auto</span> parts = node.as!string().split(<span class="d_string">":"</span>);
<span class="d_keyword">try</span> <span class="d_comment">//If this throws, the D:YAML will handle it and throw a YAMLException.
{ </span> <span class="d_keyword">return</span> MyStruct(to!<span class="d_keyword">int</span>(parts[0]), to!<span class="d_keyword">int</span>(parts[1]), to!<span class="d_keyword">int</span>(parts[2]));
<span class="d_keyword">return</span> MyStruct(to!<span class="d_keyword">int</span>(parts[0]), to!<span class="d_keyword">int</span>(parts[1]), to!<span class="d_keyword">int</span>(parts[2]));
}
<span class="d_keyword">catch</span>(Exception e)
{
<span class="d_keyword">throw</span> <span class="d_keyword">new</span> ConstructorException(<span class="d_string">"Could not construct MyStruct: "</span> ~ e.msg,
start, end);
}
} }
<span class="d_keyword">void</span> main() <span class="d_keyword">void</span> main()
@ -152,7 +151,7 @@
</p> </p>
</dd> </dd>
<dt class="d_decl">void <a name="addConstructorSequence"></a><span class="ddoc_psymbol">addConstructorSequence</span>(T)(in string <b>tag</b>, T function(Mark, Mark, ref Node) <b>ctor</b>); <dt class="d_decl">void <a name="addConstructorSequence"></a><span class="ddoc_psymbol">addConstructorSequence</span>(T)(in string <b>tag</b>, T function(ref Node) <b>ctor</b>);
</dt> </dt>
<dd><p>Add a constructor function from sequence. <dd><p>Add a constructor function from sequence.
</p> </p>
@ -169,19 +168,11 @@
<span class="d_keyword">int</span> x, y, z; <span class="d_keyword">int</span> x, y, z;
} }
MyStruct constructMyStructSequence(Mark start, Mark end, <span class="d_keyword">ref</span> Node node) MyStruct constructMyStructSequence(<span class="d_keyword">ref</span> Node node)
{ {
<span class="d_comment">//node is guaranteed to be sequence. <span class="d_comment">//node is guaranteed to be sequence.
</span> <span class="d_comment">//!mystruct [x, y, z] </span> <span class="d_comment">//!mystruct [x, y, z]
</span> <span class="d_keyword">try</span> </span> <span class="d_keyword">return</span> MyStruct(node[0].as!<span class="d_keyword">int</span>, node[1].as!<span class="d_keyword">int</span>, node[2].as!<span class="d_keyword">int</span>);
{
<span class="d_keyword">return</span> MyStruct(node[0].as!<span class="d_keyword">int</span>, node[1].as!<span class="d_keyword">int</span>, node[2].as!<span class="d_keyword">int</span>);
}
<span class="d_keyword">catch</span>(NodeException e)
{
<span class="d_keyword">throw</span> <span class="d_keyword">new</span> ConstructorException(<span class="d_string">"Could not construct MyStruct: "</span> ~ e.msg,
start, end);
}
} }
<span class="d_keyword">void</span> main() <span class="d_keyword">void</span> main()
@ -196,7 +187,7 @@
</p> </p>
</dd> </dd>
<dt class="d_decl">void <a name="addConstructorMapping"></a><span class="ddoc_psymbol">addConstructorMapping</span>(T)(in string <b>tag</b>, T function(Mark, Mark, ref Node) <b>ctor</b>); <dt class="d_decl">void <a name="addConstructorMapping"></a><span class="ddoc_psymbol">addConstructorMapping</span>(T)(in string <b>tag</b>, T function(ref Node) <b>ctor</b>);
</dt> </dt>
<dd><p>Add a constructor function from a mapping. <dd><p>Add a constructor function from a mapping.
</p> </p>
@ -213,19 +204,11 @@
<span class="d_keyword">int</span> x, y, z; <span class="d_keyword">int</span> x, y, z;
} }
MyStruct constructMyStructMapping(Mark start, Mark end, <span class="d_keyword">ref</span> Node node) MyStruct constructMyStructMapping(<span class="d_keyword">ref</span> Node node)
{ {
<span class="d_comment">//node is guaranteed to be mapping. <span class="d_comment">//node is guaranteed to be mapping.
</span> <span class="d_comment">//!mystruct {"x": x, "y": y, "z": z} </span> <span class="d_comment">//!mystruct {"x": x, "y": y, "z": z}
</span> <span class="d_keyword">try</span> </span> <span class="d_keyword">return</span> MyStruct(node[<span class="d_string">"x"</span>].as!<span class="d_keyword">int</span>, node[<span class="d_string">"y"</span>].as!<span class="d_keyword">int</span>, node[<span class="d_string">"z"</span>].as!<span class="d_keyword">int</span>);
{
<span class="d_keyword">return</span> MyStruct(node[<span class="d_string">"x"</span>].as!<span class="d_keyword">int</span>, node[<span class="d_string">"y"</span>].as!<span class="d_keyword">int</span>, node[<span class="d_string">"z"</span>].as!<span class="d_keyword">int</span>);
}
<span class="d_keyword">catch</span>(NodeException e)
{
<span class="d_keyword">throw</span> <span class="d_keyword">new</span> ConstructorException(<span class="d_string">"Could not construct MyStruct: "</span> ~ e.msg,
start, end);
}
} }
<span class="d_keyword">void</span> main() <span class="d_keyword">void</span> main()
@ -242,72 +225,72 @@
</dd> </dd>
</dl> </dl>
</dd> </dd>
<dt class="d_decl">YAMLNull <a name="constructNull"></a><span class="ddoc_psymbol">constructNull</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>); <dt class="d_decl">YAMLNull <a name="constructNull"></a><span class="ddoc_psymbol">constructNull</span>(ref Node <b>node</b>);
</dt> </dt>
<dd><p>Construct a <b>null</b> <b>node</b>.</p> <dd><p>Construct a <b>null</b> <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">YAMLMerge <a name="constructMerge"></a><span class="ddoc_psymbol">constructMerge</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>); <dt class="d_decl">YAMLMerge <a name="constructMerge"></a><span class="ddoc_psymbol">constructMerge</span>(ref Node <b>node</b>);
</dt> </dt>
<dd><p>Construct a merge <b>node</b> - a <b>node</b> that merges another <b>node</b> into a mapping.</p> <dd><p>Construct a merge <b>node</b> - a <b>node</b> that merges another <b>node</b> into a mapping.</p>
</dd> </dd>
<dt class="d_decl">bool <a name="constructBool"></a><span class="ddoc_psymbol">constructBool</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>); <dt class="d_decl">bool <a name="constructBool"></a><span class="ddoc_psymbol">constructBool</span>(ref Node <b>node</b>);
</dt> </dt>
<dd><p>Construct a boolean <b>node</b>.</p> <dd><p>Construct a boolean <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">long <a name="constructLong"></a><span class="ddoc_psymbol">constructLong</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>); <dt class="d_decl">long <a name="constructLong"></a><span class="ddoc_psymbol">constructLong</span>(ref Node <b>node</b>);
</dt> </dt>
<dd><p>Construct an integer (long) <b>node</b>.</p> <dd><p>Construct an integer (long) <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">real <a name="constructReal"></a><span class="ddoc_psymbol">constructReal</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>); <dt class="d_decl">real <a name="constructReal"></a><span class="ddoc_psymbol">constructReal</span>(ref Node <b>node</b>);
</dt> </dt>
<dd><p>Construct a floating point (real) <b>node</b>.</p> <dd><p>Construct a floating point (real) <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">ubyte[] <a name="constructBinary"></a><span class="ddoc_psymbol">constructBinary</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>); <dt class="d_decl">ubyte[] <a name="constructBinary"></a><span class="ddoc_psymbol">constructBinary</span>(ref Node <b>node</b>);
</dt> </dt>
<dd><p>Construct a binary (base64) <b>node</b>.</p> <dd><p>Construct a binary (base64) <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">SysTime <a name="constructTimestamp"></a><span class="ddoc_psymbol">constructTimestamp</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>); <dt class="d_decl">SysTime <a name="constructTimestamp"></a><span class="ddoc_psymbol">constructTimestamp</span>(ref Node <b>node</b>);
</dt> </dt>
<dd><p>Construct a timestamp (SysTime) <b>node</b>.</p> <dd><p>Construct a timestamp (SysTime) <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">string <a name="constructString"></a><span class="ddoc_psymbol">constructString</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>); <dt class="d_decl">string <a name="constructString"></a><span class="ddoc_psymbol">constructString</span>(ref Node <b>node</b>);
</dt> </dt>
<dd><p>Construct a string <b>node</b>.</p> <dd><p>Construct a string <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">Pair[] <a name="getPairs"></a><span class="ddoc_psymbol">getPairs</span>(string <b>type</b>, Mark <b>start</b>, Mark <b>end</b>, Node[] <b>nodes</b>); <dt class="d_decl">Pair[] <a name="getPairs"></a><span class="ddoc_psymbol">getPairs</span>(string <b>type</b>, Node[] <b>nodes</b>);
</dt> </dt>
<dd><p>Convert a sequence of single-element mappings into a sequence of pairs.</p> <dd><p>Convert a sequence of single-element mappings into a sequence of pairs.</p>
</dd> </dd>
<dt class="d_decl">Pair[] <a name="constructOrderedMap"></a><span class="ddoc_psymbol">constructOrderedMap</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>); <dt class="d_decl">Pair[] <a name="constructOrderedMap"></a><span class="ddoc_psymbol">constructOrderedMap</span>(ref Node <b>node</b>);
</dt> </dt>
<dd><p>Construct an ordered map (ordered sequence of key:value pairs without duplicates) <b>node</b>.</p> <dd><p>Construct an ordered map (ordered sequence of key:value pairs without duplicates) <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">Pair[] <a name="constructPairs"></a><span class="ddoc_psymbol">constructPairs</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>); <dt class="d_decl">Pair[] <a name="constructPairs"></a><span class="ddoc_psymbol">constructPairs</span>(ref Node <b>node</b>);
</dt> </dt>
<dd><p>Construct a pairs (ordered sequence of key: value pairs allowing duplicates) <b>node</b>.</p> <dd><p>Construct a pairs (ordered sequence of key: value pairs allowing duplicates) <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">Node[] <a name="constructSet"></a><span class="ddoc_psymbol">constructSet</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>); <dt class="d_decl">Node[] <a name="constructSet"></a><span class="ddoc_psymbol">constructSet</span>(ref Node <b>node</b>);
</dt> </dt>
<dd><p>Construct a set <b>node</b>.</p> <dd><p>Construct a set <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">Node[] <a name="constructSequence"></a><span class="ddoc_psymbol">constructSequence</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>); <dt class="d_decl">Node[] <a name="constructSequence"></a><span class="ddoc_psymbol">constructSequence</span>(ref Node <b>node</b>);
</dt> </dt>
<dd><p>Construct a sequence (array) <b>node</b>.</p> <dd><p>Construct a sequence (array) <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">Pair[] <a name="constructMap"></a><span class="ddoc_psymbol">constructMap</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>); <dt class="d_decl">Pair[] <a name="constructMap"></a><span class="ddoc_psymbol">constructMap</span>(ref Node <b>node</b>);
</dt> </dt>
<dd><p>Construct an unordered map (unordered set of key: value pairs without duplicates) <b>node</b>.</p> <dd><p>Construct an unordered map (unordered set of key: value pairs without duplicates) <b>node</b>.</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 Nov 16, 2011. Last updated on Nov 17, 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 Nov 16, 2011. Last updated on Nov 17, 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 Nov 16, 2011. Last updated on Nov 17, 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>

File diff suppressed because one or more lines are too long

View file

@ -48,12 +48,12 @@
<div class="section" id="custom-yaml-data-types"> <div class="section" id="custom-yaml-data-types">
<h1>Custom YAML data types<a class="headerlink" href="#custom-yaml-data-types" title="Permalink to this headline"></a></h1> <h1>Custom YAML data types<a class="headerlink" href="#custom-yaml-data-types" title="Permalink to this headline"></a></h1>
<p>Sometimes you need to serialize complex data types such as classes. To do this <p>Sometimes you need to serialize complex data types such as classes. To do this
you could use plain nodes such as mappings with class data members. However, you could use plain nodes such as mappings with class data members. YAML also
YAML supports custom types with identifiers called <em>tags</em>. That is the topic of supports custom types with identifiers called <em>tags</em>. That is the topic of this
this tutorial.</p> tutorial.</p>
<p>Each YAML node has a tag specifying its type. For instance: strings use the tag <p>Each YAML node has a tag specifying its type. For instance: strings use the tag
<tt class="docutils literal"><span class="pre">tag:yaml.org,2002:str</span></tt>. Tags of most default types are <em>implicitly resolved</em> <tt class="docutils literal"><span class="pre">tag:yaml.org,2002:str</span></tt>. Tags of most default types are <em>implicitly resolved</em>
during parsing, so you don&#8217;t need to specify tag for each float, integer, etc. during parsing - you don&#8217;t need to specify tag for each float, integer, etc.
D:YAML can also implicitly resolve custom tags, as we will show later.</p> D:YAML can also implicitly resolve custom tags, as we will show later.</p>
<div class="section" id="constructor"> <div class="section" id="constructor">
<h2>Constructor<a class="headerlink" href="#constructor" title="Permalink to this headline"></a></h2> <h2>Constructor<a class="headerlink" href="#constructor" title="Permalink to this headline"></a></h2>
@ -63,7 +63,7 @@ functions to process each supported tag. These are supplied by the user using
the <em>addConstructorXXX()</em> methods, where <em>XXX</em> is <em>Scalar</em>, <em>Sequence</em> or the <em>addConstructorXXX()</em> methods, where <em>XXX</em> is <em>Scalar</em>, <em>Sequence</em> or
<em>Mapping</em>. <em>Constructor</em> is then passed to <em>Loader</em>, which parses YAML input.</p> <em>Mapping</em>. <em>Constructor</em> is then passed to <em>Loader</em>, which parses YAML input.</p>
<p>Struct types have no specific requirements for YAML support. Class types should <p>Struct types have no specific requirements for YAML support. Class types should
define the <em>opEquals()</em> operator, as this is used in equality comparisons of define the <em>opEquals()</em> operator - this is used in equality comparisons of
nodes. Default class <em>opEquals()</em> compares references, which means two identical nodes. Default class <em>opEquals()</em> compares references, which means two identical
objects might be considered unequal. (Default struct <em>opEquals()</em> compares objects might be considered unequal. (Default struct <em>opEquals()</em> compares
byte-by-byte, sometimes you might want to override that as well.)</p> byte-by-byte, sometimes you might want to override that as well.)</p>
@ -77,21 +77,22 @@ following struct:</p>
<span class="p">}</span> <span class="p">}</span>
</pre></div> </pre></div>
</div> </div>
<p>First, we need a function to construct our data type. It must take two <em>Mark</em> <p>First, we need a function to construct our data type. The function will take a
structs, which store position of the node in the file, and a reference to <em>Node</em> reference to <em>Node</em> to construct from. The node is guaranteed to contain either
to construct from. The node is guaranteed to contain either a <em>string</em>, an array a <em>string</em>, an array of <em>Node</em> or of <em>Node.Pair</em>, depending on whether we&#8217;re
of <em>Node</em> or of <em>Node.Pair</em>, depending on whether we&#8217;re constructing our value constructing our value from a scalar, sequence, or mapping, respectively.
from a scalar, sequence, or mapping, respectively. In this tutorial, we have If this function throws any exception, D:YAML handles it and adds its message
functions to construct a color from a scalar, using CSS-like format, RRGGBB, or to a <em>YAMLException</em> that will be thrown when loading the file.</p>
from a mapping, where we use the following format: {r:RRR, g:GGG, b:BBB} . Code <p>In this tutorial, we have functions to construct a color from a scalar, using
of these functions:</p> CSS-like format, RRGGBB, or from a mapping, where we use the following format:
<div class="highlight-d"><div class="highlight"><pre><span class="n">Color</span> <span class="n">constructColorScalar</span><span class="p">(</span><span class="n">Mark</span> <span class="n">start</span><span class="p">,</span> <span class="n">Mark</span> <span class="n">end</span><span class="p">,</span> <span class="k">ref</span> <span class="n">Node</span> <span class="n">node</span><span class="p">)</span> {r:RRR, g:GGG, b:BBB} . Code of these functions:</p>
<div class="highlight-d"><div class="highlight"><pre><span class="n">Color</span> <span class="n">constructColorScalar</span><span class="p">(</span><span class="k">ref</span> <span class="n">Node</span> <span class="n">node</span><span class="p">)</span>
<span class="p">{</span> <span class="p">{</span>
<span class="nb">string</span> <span class="n">value</span> <span class="p">=</span> <span class="n">node</span><span class="p">.</span><span class="n">as</span><span class="p">!</span><span class="nb">string</span><span class="p">;</span> <span class="nb">string</span> <span class="n">value</span> <span class="p">=</span> <span class="n">node</span><span class="p">.</span><span class="n">as</span><span class="p">!</span><span class="nb">string</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="n">value</span><span class="p">.</span><span class="n">length</span> <span class="p">!=</span> <span class="mi">6</span><span class="p">)</span> <span class="k">if</span><span class="p">(</span><span class="n">value</span><span class="p">.</span><span class="n">length</span> <span class="p">!=</span> <span class="mi">6</span><span class="p">)</span>
<span class="p">{</span> <span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="n">ConstructorException</span><span class="p">(</span><span class="s">&quot;Invalid color: &quot;</span> <span class="p">~</span> <span class="n">value</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">);</span> <span class="k">throw</span> <span class="k">new</span> <span class="n">Exception</span><span class="p">(</span><span class="s">&quot;Invalid color: &quot;</span> <span class="p">~</span> <span class="n">value</span><span class="p">);</span>
<span class="p">}</span> <span class="p">}</span>
<span class="c1">//We don&#39;t need to check for uppercase chars this way.</span> <span class="c1">//We don&#39;t need to check for uppercase chars this way.</span>
<span class="n">value</span> <span class="p">=</span> <span class="n">value</span><span class="p">.</span><span class="n">toLower</span><span class="p">();</span> <span class="n">value</span> <span class="p">=</span> <span class="n">value</span><span class="p">.</span><span class="n">toLower</span><span class="p">();</span>
@ -101,7 +102,7 @@ of these functions:</p>
<span class="p">{</span> <span class="p">{</span>
<span class="k">if</span><span class="p">(!</span><span class="n">std</span><span class="p">.</span><span class="n">ascii</span><span class="p">.</span><span class="n">isHexDigit</span><span class="p">(</span><span class="n">c</span><span class="p">))</span> <span class="k">if</span><span class="p">(!</span><span class="n">std</span><span class="p">.</span><span class="n">ascii</span><span class="p">.</span><span class="n">isHexDigit</span><span class="p">(</span><span class="n">c</span><span class="p">))</span>
<span class="p">{</span> <span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="n">ConstructorException</span><span class="p">(</span><span class="s">&quot;Invalid color: &quot;</span> <span class="p">~</span> <span class="n">value</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">);</span> <span class="k">throw</span> <span class="k">new</span> <span class="n">Exception</span><span class="p">(</span><span class="s">&quot;Invalid color: &quot;</span> <span class="p">~</span> <span class="n">value</span><span class="p">);</span>
<span class="p">}</span> <span class="p">}</span>
<span class="k">if</span><span class="p">(</span><span class="n">std</span><span class="p">.</span><span class="n">ascii</span><span class="p">.</span><span class="n">isDigit</span><span class="p">(</span><span class="n">c</span><span class="p">))</span> <span class="k">if</span><span class="p">(</span><span class="n">std</span><span class="p">.</span><span class="n">ascii</span><span class="p">.</span><span class="n">isDigit</span><span class="p">(</span><span class="n">c</span><span class="p">))</span>
@ -119,21 +120,16 @@ of these functions:</p>
<span class="k">return</span> <span class="n">result</span><span class="p">;</span> <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span> <span class="p">}</span>
<span class="n">Color</span> <span class="n">constructColorMapping</span><span class="p">(</span><span class="n">Mark</span> <span class="n">start</span><span class="p">,</span> <span class="n">Mark</span> <span class="n">end</span><span class="p">,</span> <span class="k">ref</span> <span class="n">Node</span> <span class="n">node</span><span class="p">)</span> <span class="n">Color</span> <span class="n">constructColorMapping</span><span class="p">(</span><span class="k">ref</span> <span class="n">Node</span> <span class="n">node</span><span class="p">)</span>
<span class="p">{</span> <span class="p">{</span>
<span class="kt">ubyte</span> <span class="n">r</span><span class="p">,</span><span class="n">g</span><span class="p">,</span><span class="n">b</span><span class="p">;</span> <span class="kt">ubyte</span> <span class="n">r</span><span class="p">,</span><span class="n">g</span><span class="p">,</span><span class="n">b</span><span class="p">;</span>
<span class="c1">//Might throw if a value is missing is not an integer, or is out of range.</span> <span class="c1">//Might throw if a value is missing is not an integer, or is out of range.</span>
<span class="k">try</span> <span class="c1">//If this happens, D:YAML will handle the exception and use its message</span>
<span class="p">{</span> <span class="c1">//in a YAMLException thrown when loading.</span>
<span class="n">r</span> <span class="p">=</span> <span class="n">node</span><span class="p">[</span><span class="s">&quot;r&quot;</span><span class="p">].</span><span class="n">as</span><span class="p">!</span><span class="kt">ubyte</span><span class="p">;</span> <span class="n">r</span> <span class="p">=</span> <span class="n">node</span><span class="p">[</span><span class="s">&quot;r&quot;</span><span class="p">].</span><span class="n">as</span><span class="p">!</span><span class="kt">ubyte</span><span class="p">;</span>
<span class="n">g</span> <span class="p">=</span> <span class="n">node</span><span class="p">[</span><span class="s">&quot;g&quot;</span><span class="p">].</span><span class="n">as</span><span class="p">!</span><span class="kt">ubyte</span><span class="p">;</span> <span class="n">g</span> <span class="p">=</span> <span class="n">node</span><span class="p">[</span><span class="s">&quot;g&quot;</span><span class="p">].</span><span class="n">as</span><span class="p">!</span><span class="kt">ubyte</span><span class="p">;</span>
<span class="n">b</span> <span class="p">=</span> <span class="n">node</span><span class="p">[</span><span class="s">&quot;b&quot;</span><span class="p">].</span><span class="n">as</span><span class="p">!</span><span class="kt">ubyte</span><span class="p">;</span> <span class="n">b</span> <span class="p">=</span> <span class="n">node</span><span class="p">[</span><span class="s">&quot;b&quot;</span><span class="p">].</span><span class="n">as</span><span class="p">!</span><span class="kt">ubyte</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">catch</span><span class="p">(</span><span class="n">NodeException</span> <span class="n">e</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="n">ConstructorException</span><span class="p">(</span><span class="s">&quot;Invalid color: &quot;</span> <span class="p">~</span> <span class="n">e</span><span class="p">.</span><span class="n">msg</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">Color</span><span class="p">(</span><span class="k">cast</span><span class="p">(</span><span class="kt">ubyte</span><span class="p">)</span><span class="n">r</span><span class="p">,</span> <span class="k">cast</span><span class="p">(</span><span class="kt">ubyte</span><span class="p">)</span><span class="n">g</span><span class="p">,</span> <span class="k">cast</span><span class="p">(</span><span class="kt">ubyte</span><span class="p">)</span><span class="n">b</span><span class="p">);</span> <span class="k">return</span> <span class="n">Color</span><span class="p">(</span><span class="k">cast</span><span class="p">(</span><span class="kt">ubyte</span><span class="p">)</span><span class="n">r</span><span class="p">,</span> <span class="k">cast</span><span class="p">(</span><span class="kt">ubyte</span><span class="p">)</span><span class="n">g</span><span class="p">,</span> <span class="k">cast</span><span class="p">(</span><span class="kt">ubyte</span><span class="p">)</span><span class="n">b</span><span class="p">);</span>
<span class="p">}</span> <span class="p">}</span>
@ -368,7 +364,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 Nov 16, 2011. Last updated on Nov 17, 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 Nov 16, 2011. Last updated on Nov 17, 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 Nov 16, 2011. Last updated on Nov 17, 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

@ -3,13 +3,13 @@ Custom YAML data types
====================== ======================
Sometimes you need to serialize complex data types such as classes. To do this Sometimes you need to serialize complex data types such as classes. To do this
you could use plain nodes such as mappings with class data members. However, you could use plain nodes such as mappings with class data members. YAML also
YAML supports custom types with identifiers called *tags*. That is the topic of supports custom types with identifiers called *tags*. That is the topic of this
this tutorial. tutorial.
Each YAML node has a tag specifying its type. For instance: strings use the tag Each YAML node has a tag specifying its type. For instance: strings use the tag
``tag:yaml.org,2002:str``. Tags of most default types are *implicitly resolved* ``tag:yaml.org,2002:str``. Tags of most default types are *implicitly resolved*
during parsing, so you don't need to specify tag for each float, integer, etc. during parsing - you don't need to specify tag for each float, integer, etc.
D:YAML can also implicitly resolve custom tags, as we will show later. D:YAML can also implicitly resolve custom tags, as we will show later.
@ -24,7 +24,7 @@ the *addConstructorXXX()* methods, where *XXX* is *Scalar*, *Sequence* or
*Mapping*. *Constructor* is then passed to *Loader*, which parses YAML input. *Mapping*. *Constructor* is then passed to *Loader*, which parses YAML input.
Struct types have no specific requirements for YAML support. Class types should Struct types have no specific requirements for YAML support. Class types should
define the *opEquals()* operator, as this is used in equality comparisons of define the *opEquals()* operator - this is used in equality comparisons of
nodes. Default class *opEquals()* compares references, which means two identical nodes. Default class *opEquals()* compares references, which means two identical
objects might be considered unequal. (Default struct *opEquals()* compares objects might be considered unequal. (Default struct *opEquals()* compares
byte-by-byte, sometimes you might want to override that as well.) byte-by-byte, sometimes you might want to override that as well.)
@ -41,24 +41,26 @@ following struct:
ubyte blue; ubyte blue;
} }
First, we need a function to construct our data type. It must take two *Mark* First, we need a function to construct our data type. The function will take a
structs, which store position of the node in the file, and a reference to *Node* reference to *Node* to construct from. The node is guaranteed to contain either
to construct from. The node is guaranteed to contain either a *string*, an array a *string*, an array of *Node* or of *Node.Pair*, depending on whether we're
of *Node* or of *Node.Pair*, depending on whether we're constructing our value constructing our value from a scalar, sequence, or mapping, respectively.
from a scalar, sequence, or mapping, respectively. In this tutorial, we have If this function throws any exception, D:YAML handles it and adds its message
functions to construct a color from a scalar, using CSS-like format, RRGGBB, or to a *YAMLException* that will be thrown when loading the file.
from a mapping, where we use the following format: {r:RRR, g:GGG, b:BBB} . Code
of these functions: In this tutorial, we have functions to construct a color from a scalar, using
CSS-like format, RRGGBB, or from a mapping, where we use the following format:
{r:RRR, g:GGG, b:BBB} . Code of these functions:
.. code-block:: d .. code-block:: d
Color constructColorScalar(Mark start, Mark end, ref Node node) Color constructColorScalar(ref Node node)
{ {
string value = node.as!string; string value = node.as!string;
if(value.length != 6) if(value.length != 6)
{ {
throw new ConstructorException("Invalid color: " ~ value, start, end); throw new Exception("Invalid color: " ~ value);
} }
//We don't need to check for uppercase chars this way. //We don't need to check for uppercase chars this way.
value = value.toLower(); value = value.toLower();
@ -68,7 +70,7 @@ of these functions:
{ {
if(!std.ascii.isHexDigit(c)) if(!std.ascii.isHexDigit(c))
{ {
throw new ConstructorException("Invalid color: " ~ value, start, end); throw new Exception("Invalid color: " ~ value);
} }
if(std.ascii.isDigit(c)) if(std.ascii.isDigit(c))
@ -86,21 +88,16 @@ of these functions:
return result; return result;
} }
Color constructColorMapping(Mark start, Mark end, ref Node node) Color constructColorMapping(ref Node node)
{ {
ubyte r,g,b; ubyte r,g,b;
//Might throw if a value is missing is not an integer, or is out of range. //Might throw if a value is missing is not an integer, or is out of range.
try //If this happens, D:YAML will handle the exception and use its message
{ //in a YAMLException thrown when loading.
r = node["r"].as!ubyte; r = node["r"].as!ubyte;
g = node["g"].as!ubyte; g = node["g"].as!ubyte;
b = node["b"].as!ubyte; b = node["b"].as!ubyte;
}
catch(NodeException e)
{
throw new ConstructorException("Invalid color: " ~ e.msg, start, end);
}
return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b); return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b);
} }

View file

@ -34,7 +34,7 @@ import dyaml.style;
* *
* Can be thrown by custom constructor functions. * Can be thrown by custom constructor functions.
*/ */
class ConstructorException : YAMLException package class ConstructorException : YAMLException
{ {
/** /**
* Construct a ConstructorException. * Construct a ConstructorException.
@ -70,11 +70,11 @@ final class Constructor
{ {
private: private:
///Constructor functions from scalars. ///Constructor functions from scalars.
Node.Value delegate(Mark, Mark, ref Node)[Tag] fromScalar_; Node.Value delegate(ref Node)[Tag] fromScalar_;
///Constructor functions from sequences. ///Constructor functions from sequences.
Node.Value delegate(Mark, Mark, ref Node)[Tag] fromSequence_; Node.Value delegate(ref Node)[Tag] fromSequence_;
///Constructor functions from mappings. ///Constructor functions from mappings.
Node.Value delegate(Mark, Mark, ref Node)[Tag] fromMapping_; Node.Value delegate(ref Node)[Tag] fromMapping_;
public: public:
/** /**
@ -122,10 +122,14 @@ final class Constructor
/** /**
* Add a constructor function from scalar. * Add a constructor function from scalar.
* *
* The function must take two Marks (start and end positions of * The function must take a reference to Node to construct from.
* the node in file) and a reference to Node to construct from.
* The node contains a string for scalars, Node[] for sequences and * The node contains a string for scalars, Node[] for sequences and
* Node.Pair[] for mappings. * Node.Pair[] for mappings.
*
* Any exception thrown by this function will be caught by D:YAML and
* its message will be added to a YAMLException that will also tell the
* user which type failed to construct, and position in the file.
*
* The value returned by this function will be stored in the resulting node. * The value returned by this function will be stored in the resulting node.
* *
* Only one constructor function can be set for one tag. * Only one constructor function can be set for one tag.
@ -145,20 +149,13 @@ final class Constructor
* int x, y, z; * int x, y, z;
* } * }
* *
* MyStruct constructMyStructScalar(Mark start, Mark end, ref Node node) * MyStruct constructMyStructScalar(ref Node node)
* { * {
* //Guaranteed to be string as we construct from scalar. * //Guaranteed to be string as we construct from scalar.
* //!mystruct x:y:z * //!mystruct x:y:z
* auto parts = node.as!string().split(":"); * auto parts = node.as!string().split(":");
* try * //If this throws, the D:YAML will handle it and throw a YAMLException.
* { * return MyStruct(to!int(parts[0]), to!int(parts[1]), to!int(parts[2]));
* return MyStruct(to!int(parts[0]), to!int(parts[1]), to!int(parts[2]));
* }
* catch(Exception e)
* {
* throw new ConstructorException("Could not construct MyStruct: " ~ e.msg,
* start, end);
* }
* } * }
* *
* void main() * void main()
@ -171,7 +168,7 @@ final class Constructor
* } * }
* -------------------- * --------------------
*/ */
void addConstructorScalar(T)(in string tag, T function(Mark, Mark, ref Node) ctor) void addConstructorScalar(T)(in string tag, T function(ref Node) ctor)
{ {
const t = Tag(tag); const t = Tag(tag);
auto deleg = addConstructor!T(t, ctor); auto deleg = addConstructor!T(t, ctor);
@ -195,19 +192,11 @@ final class Constructor
* int x, y, z; * int x, y, z;
* } * }
* *
* MyStruct constructMyStructSequence(Mark start, Mark end, ref Node node) * MyStruct constructMyStructSequence(ref Node node)
* { * {
* //node is guaranteed to be sequence. * //node is guaranteed to be sequence.
* //!mystruct [x, y, z] * //!mystruct [x, y, z]
* try * return MyStruct(node[0].as!int, node[1].as!int, node[2].as!int);
* {
* return MyStruct(node[0].as!int, node[1].as!int, node[2].as!int);
* }
* catch(NodeException e)
* {
* throw new ConstructorException("Could not construct MyStruct: " ~ e.msg,
* start, end);
* }
* } * }
* *
* void main() * void main()
@ -220,7 +209,7 @@ final class Constructor
* } * }
* -------------------- * --------------------
*/ */
void addConstructorSequence(T)(in string tag, T function(Mark, Mark, ref Node) ctor) void addConstructorSequence(T)(in string tag, T function(ref Node) ctor)
{ {
const t = Tag(tag); const t = Tag(tag);
auto deleg = addConstructor!T(t, ctor); auto deleg = addConstructor!T(t, ctor);
@ -244,19 +233,11 @@ final class Constructor
* int x, y, z; * int x, y, z;
* } * }
* *
* MyStruct constructMyStructMapping(Mark start, Mark end, ref Node node) * MyStruct constructMyStructMapping(ref Node node)
* { * {
* //node is guaranteed to be mapping. * //node is guaranteed to be mapping.
* //!mystruct {"x": x, "y": y, "z": z} * //!mystruct {"x": x, "y": y, "z": z}
* try * return MyStruct(node["x"].as!int, node["y"].as!int, node["z"].as!int);
* {
* return MyStruct(node["x"].as!int, node["y"].as!int, node["z"].as!int);
* }
* catch(NodeException e)
* {
* throw new ConstructorException("Could not construct MyStruct: " ~ e.msg,
* start, end);
* }
* } * }
* *
* void main() * void main()
@ -269,7 +250,7 @@ final class Constructor
* } * }
* -------------------- * --------------------
*/ */
void addConstructorMapping(T)(in string tag, T function(Mark, Mark, ref Node) ctor) void addConstructorMapping(T)(in string tag, T function(ref Node) ctor)
{ {
const t = Tag(tag); const t = Tag(tag);
auto deleg = addConstructor!T(t, ctor); auto deleg = addConstructor!T(t, ctor);
@ -295,17 +276,25 @@ final class Constructor
enforce((tag in *delegates!T) !is null, enforce((tag in *delegates!T) !is null,
new Error("No constructor function for tag " ~ tag.get(), start, end)); new Error("No constructor function for tag " ~ tag.get(), start, end));
Node node = Node(value); Node node = Node(value);
static if(is(U : ScalarStyle)) try
{ {
return Node.rawNode((*delegates!T)[tag](start, end, node), start, tag, static if(is(U : ScalarStyle))
style, CollectionStyle.Invalid); {
return Node.rawNode((*delegates!T)[tag](node), start, tag,
style, CollectionStyle.Invalid);
}
else static if(is(U : CollectionStyle))
{
return Node.rawNode((*delegates!T)[tag](node), start, tag,
ScalarStyle.Invalid, style);
}
else static assert(false);
} }
else static if(is(U : CollectionStyle)) catch(Exception e)
{ {
return Node.rawNode((*delegates!T)[tag](start, end, node), start, tag, throw new Error("Error constructing " ~ typeid(T).toString()
ScalarStyle.Invalid, style); ~ ":\n" ~ e.msg, start, end);
} }
else static assert(false);
} }
private: private:
@ -315,7 +304,7 @@ final class Constructor
* Params: tag = Tag for the function to handle. * Params: tag = Tag for the function to handle.
* ctor = Constructor function. * ctor = Constructor function.
*/ */
auto addConstructor(T)(in Tag tag, T function(Mark, Mark, ref Node) ctor) auto addConstructor(T)(in Tag tag, T function(ref Node) ctor)
{ {
assert((tag in fromScalar_) is null && assert((tag in fromScalar_) is null &&
(tag in fromSequence_) is null && (tag in fromSequence_) is null &&
@ -323,10 +312,10 @@ final class Constructor
"Constructor function for tag " ~ tag.get ~ " is already " "Constructor function for tag " ~ tag.get ~ " is already "
"specified. Can't specify another one."); "specified. Can't specify another one.");
return (Mark s, Mark e, ref Node n) return (ref Node n)
{ {
static if(Node.Value.allowed!T){return Node.Value(ctor(s,e,n));} static if(Node.Value.allowed!T){return Node.Value(ctor(n));}
else {return Node.userValue(ctor(s,e,n));} else {return Node.userValue(ctor(n));}
}; };
} }
@ -342,30 +331,30 @@ final class Constructor
///Construct a null node. ///Construct a null node.
YAMLNull constructNull(Mark start, Mark end, ref Node node) YAMLNull constructNull(ref Node node)
{ {
return YAMLNull(); return YAMLNull();
} }
///Construct a merge node - a node that merges another node into a mapping. ///Construct a merge node - a node that merges another node into a mapping.
YAMLMerge constructMerge(Mark start, Mark end, ref Node node) YAMLMerge constructMerge(ref Node node)
{ {
return YAMLMerge(); return YAMLMerge();
} }
///Construct a boolean node. ///Construct a boolean node.
bool constructBool(Mark start, Mark end, ref Node node) bool constructBool(ref Node node)
{ {
static yes = ["yes", "true", "on"]; static yes = ["yes", "true", "on"];
static no = ["no", "false", "off"]; static no = ["no", "false", "off"];
string value = node.as!string().toLower(); string value = node.as!string().toLower();
if(yes.canFind(value)){return true;} if(yes.canFind(value)){return true;}
if(no.canFind(value)) {return false;} if(no.canFind(value)) {return false;}
throw new Error("Unable to parse boolean value: " ~ value, start, end); throw new Exception("Unable to parse boolean value: " ~ value);
} }
///Construct an integer (long) node. ///Construct an integer (long) node.
long constructLong(Mark start, Mark end, ref Node node) long constructLong(ref Node node)
{ {
string value = node.as!string().replace("_", ""); string value = node.as!string().replace("_", "");
const char c = value[0]; const char c = value[0];
@ -375,7 +364,7 @@ long constructLong(Mark start, Mark end, ref Node node)
value = value[1 .. $]; value = value[1 .. $];
} }
enforce(value != "", new Error("Unable to parse float value: " ~ value, start, end)); enforce(value != "", new Exception("Unable to parse float value: " ~ value));
long result; long result;
try try
@ -405,7 +394,7 @@ long constructLong(Mark start, Mark end, ref Node node)
} }
catch(ConvException e) catch(ConvException e)
{ {
throw new Error("Unable to parse integer value: " ~ value, start, end); throw new Exception("Unable to parse integer value: " ~ value);
} }
return result; return result;
@ -414,7 +403,7 @@ unittest
{ {
long getLong(string str) long getLong(string str)
{ {
return constructLong(Mark(), Mark(), Node(str)); return constructLong(Node(str));
} }
string canonical = "685230"; string canonical = "685230";
@ -433,7 +422,7 @@ unittest
} }
///Construct a floating point (real) node. ///Construct a floating point (real) node.
real constructReal(Mark start, Mark end, ref Node node) real constructReal(ref Node node)
{ {
string value = node.as!string().replace("_", "").toLower(); string value = node.as!string().replace("_", "").toLower();
const char c = value[0]; const char c = value[0];
@ -444,7 +433,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 Error("Unable to parse float value: " ~ value, start, end)); new Exception("Unable to parse float value: " ~ value));
real result; real result;
try try
@ -470,7 +459,7 @@ real constructReal(Mark start, Mark end, ref Node node)
} }
catch(ConvException e) catch(ConvException e)
{ {
throw new Error("Unable to parse float value: " ~ value, start, end); throw new Exception("Unable to parse float value: " ~ value);
} }
return result; return result;
@ -484,7 +473,7 @@ unittest
real getReal(string str) real getReal(string str)
{ {
return constructReal(Mark(), Mark(), Node(str)); return constructReal(Node(str));
} }
string canonical = "6.8523015e+5"; string canonical = "6.8523015e+5";
@ -503,7 +492,7 @@ unittest
} }
///Construct a binary (base64) node. ///Construct a binary (base64) node.
ubyte[] constructBinary(Mark start, Mark end, ref Node node) ubyte[] constructBinary(ref Node node)
{ {
string value = node.as!string; string value = node.as!string;
//For an unknown reason, this must be nested to work (compiler bug?). //For an unknown reason, this must be nested to work (compiler bug?).
@ -512,12 +501,12 @@ 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 Error("Unable to decode base64 value: " ~ e.msg, start, end); throw new Exception("Unable to decode base64 value: " ~ e.msg);
} }
} }
catch(UtfException e) catch(UtfException e)
{ {
throw new Error("Unable to decode base64 value: " ~ e.msg, start, end); throw new Exception("Unable to decode base64 value: " ~ e.msg);
} }
} }
unittest unittest
@ -526,12 +515,12 @@ unittest
char[] buffer; char[] buffer;
buffer.length = 256; buffer.length = 256;
string input = cast(string)Base64.encode(test, buffer); string input = cast(string)Base64.encode(test, buffer);
auto value = constructBinary(Mark(), Mark(), Node(input)); auto value = constructBinary(Node(input));
assert(value == test); assert(value == test);
} }
///Construct a timestamp (SysTime) node. ///Construct a timestamp (SysTime) node.
SysTime constructTimestamp(Mark start, Mark end, ref Node node) SysTime constructTimestamp(ref Node node)
{ {
string value = node.as!string; string value = node.as!string;
@ -545,7 +534,7 @@ SysTime constructTimestamp(Mark start, Mark end, ref Node node)
auto matches = match(value, YMDRegexp); auto matches = match(value, YMDRegexp);
enforce(!matches.empty, enforce(!matches.empty,
new Error("Unable to parse timestamp value: " ~ value, start, end)); new Exception("Unable to parse timestamp value: " ~ value));
auto captures = matches.front.captures; auto captures = matches.front.captures;
const year = to!int(captures[1]); const year = to!int(captures[1]);
@ -592,11 +581,11 @@ SysTime constructTimestamp(Mark start, Mark end, ref Node node)
} }
catch(ConvException e) catch(ConvException e)
{ {
throw new Error("Unable to parse timestamp value " ~ value ~ " : " ~ e.msg, start, end); throw new Exception("Unable to parse timestamp value " ~ value ~ " : " ~ e.msg);
} }
catch(DateTimeException e) catch(DateTimeException e)
{ {
throw new Error("Invalid timestamp value " ~ value ~ " : " ~ e.msg, start, end); throw new Exception("Invalid timestamp value " ~ value ~ " : " ~ e.msg);
} }
assert(false, "This code should never be reached"); assert(false, "This code should never be reached");
@ -607,7 +596,7 @@ unittest
string timestamp(string value) string timestamp(string value)
{ {
return constructTimestamp(Mark(), Mark(), Node(value)).toISOString(); return constructTimestamp(Node(value)).toISOString();
} }
string canonical = "2001-12-15T02:59:43.1Z"; string canonical = "2001-12-15T02:59:43.1Z";
@ -630,21 +619,21 @@ unittest
} }
///Construct a string node. ///Construct a string node.
string constructString(Mark start, Mark end, ref Node node) string constructString(ref Node node)
{ {
return node.as!string; return node.as!string;
} }
///Convert a sequence of single-element mappings into a sequence of pairs. ///Convert a sequence of single-element mappings into a sequence of pairs.
Node.Pair[] getPairs(string type, Mark start, Mark end, Node[] nodes) Node.Pair[] getPairs(string type, Node[] nodes)
{ {
Node.Pair[] pairs; Node.Pair[] pairs;
foreach(ref node; nodes) foreach(ref node; nodes)
{ {
enforce(node.isMapping && node.length == 1, enforce(node.isMapping && node.length == 1,
new Error("While constructing " ~ type ~ new Exception("While constructing " ~ type ~
", expected a mapping with single element", start, end)); ", expected a mapping with single element"));
pairs ~= node.as!(Node.Pair[]); pairs ~= node.as!(Node.Pair[]);
} }
@ -653,9 +642,9 @@ Node.Pair[] getPairs(string type, Mark start, Mark end, Node[] nodes)
} }
///Construct an ordered map (ordered sequence of key:value pairs without duplicates) node. ///Construct an ordered map (ordered sequence of key:value pairs without duplicates) node.
Node.Pair[] constructOrderedMap(Mark start, Mark end, ref Node node) Node.Pair[] constructOrderedMap(ref Node node)
{ {
auto pairs = getPairs("ordered map", start, end, node.as!(Node[])); auto pairs = getPairs("ordered map", node.as!(Node[]));
//TODO: the map here should be replaced with something with deterministic //TODO: the map here should be replaced with something with deterministic
//memory allocation if possible. //memory allocation if possible.
@ -664,7 +653,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 Error("Duplicate entry in an ordered map", start, end)); new Exception("Duplicate entry in an ordered map"));
map[pair.key] = true; map[pair.key] = true;
} }
clear(map); clear(map);
@ -701,7 +690,7 @@ unittest
bool hasDuplicates(Node[] nodes) bool hasDuplicates(Node[] nodes)
{ {
return null !is collectException(constructOrderedMap(Mark(), Mark(), Node(nodes))); return null !is collectException(constructOrderedMap(Node(nodes)));
} }
assert(hasDuplicates(alternateTypes(8) ~ alternateTypes(2))); assert(hasDuplicates(alternateTypes(8) ~ alternateTypes(2)));
@ -713,13 +702,13 @@ unittest
} }
///Construct a pairs (ordered sequence of key: value pairs allowing duplicates) node. ///Construct a pairs (ordered sequence of key: value pairs allowing duplicates) node.
Node.Pair[] constructPairs(Mark start, Mark end, ref Node node) Node.Pair[] constructPairs(ref Node node)
{ {
return getPairs("pairs", start, end, node.as!(Node[])); return getPairs("pairs", node.as!(Node[]));
} }
///Construct a set node. ///Construct a set node.
Node[] constructSet(Mark start, Mark end, ref Node node) Node[] constructSet(ref Node node)
{ {
auto pairs = node.as!(Node.Pair[]); auto pairs = node.as!(Node.Pair[]);
@ -732,7 +721,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 Error("Duplicate entry in a set", start, end)); new Exception("Duplicate entry in a set"));
map[pair.key] = 0; map[pair.key] = 0;
nodes ~= pair.key; nodes ~= pair.key;
} }
@ -773,23 +762,23 @@ unittest
} }
assert(null !is collectException assert(null !is collectException
(constructSet(Mark(), Mark(), Node(DuplicatesShort.dup)))); (constructSet(Node(DuplicatesShort.dup))));
assert(null is collectException assert(null is collectException
(constructSet(Mark(), Mark(), Node(noDuplicatesShort.dup)))); (constructSet(Node(noDuplicatesShort.dup))));
assert(null !is collectException assert(null !is collectException
(constructSet(Mark(), Mark(), Node(DuplicatesLong.dup)))); (constructSet(Node(DuplicatesLong.dup))));
assert(null is collectException assert(null is collectException
(constructSet(Mark(), Mark(), Node(noDuplicatesLong.dup)))); (constructSet(Node(noDuplicatesLong.dup))));
} }
///Construct a sequence (array) node. ///Construct a sequence (array) node.
Node[] constructSequence(Mark start, Mark end, ref Node node) Node[] constructSequence(ref Node node)
{ {
return node.as!(Node[]); return node.as!(Node[]);
} }
///Construct an unordered map (unordered set of key: value _pairs without duplicates) node. ///Construct an unordered map (unordered set of key: value _pairs without duplicates) node.
Node.Pair[] constructMap(Mark start, Mark end, ref Node node) Node.Pair[] constructMap(ref Node node)
{ {
auto pairs = node.as!(Node.Pair[]); auto pairs = node.as!(Node.Pair[]);
//TODO: the map here should be replaced with something with deterministic //TODO: the map here should be replaced with something with deterministic
@ -800,7 +789,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 Error("Duplicate entry in a map", start, end)); new Exception("Duplicate entry in a map"));
map[pair.key] = true; map[pair.key] = true;
} }
return pairs; return pairs;
@ -818,47 +807,23 @@ struct MyStruct
int x, y, z; int x, y, z;
} }
MyStruct constructMyStructScalar(Mark start, Mark end, ref Node node) MyStruct constructMyStructScalar(ref Node node)
{ {
//Guaranteed to be string as we construct from scalar. //Guaranteed to be string as we construct from scalar.
auto parts = node.as!string().split(":"); auto parts = node.as!string().split(":");
try return MyStruct(to!int(parts[0]), to!int(parts[1]), to!int(parts[2]));
{
return MyStruct(to!int(parts[0]), to!int(parts[1]), to!int(parts[2]));
}
catch(Exception e)
{
throw new ConstructorException("Could not construct MyStruct: " ~ e.msg,
start, end);
}
} }
MyStruct constructMyStructSequence(Mark start, Mark end, ref Node node) MyStruct constructMyStructSequence(ref Node node)
{ {
//node is guaranteed to be sequence. //node is guaranteed to be sequence.
try return MyStruct(node[0].as!int, node[1].as!int, node[2].as!int);
{
return MyStruct(node[0].as!int, node[1].as!int, node[2].as!int);
}
catch(NodeException e)
{
throw new ConstructorException("Could not construct MyStruct: " ~ e.msg,
start, end);
}
} }
MyStruct constructMyStructMapping(Mark start, Mark end, ref Node node) MyStruct constructMyStructMapping(ref Node node)
{ {
//node is guaranteed to be mapping. //node is guaranteed to be mapping.
try return MyStruct(node["x"].as!int, node["y"].as!int, node["z"].as!int);
{
return MyStruct(node["x"].as!int, node["y"].as!int, node["z"].as!int);
}
catch(NodeException e)
{
throw new ConstructorException("Could not construct MyStruct: " ~ e.msg,
start, end);
}
} }
unittest unittest

View file

@ -10,13 +10,13 @@ struct Color
ubyte blue; ubyte blue;
} }
Color constructColorScalar(Mark start, Mark end, ref Node node) Color constructColorScalar(ref Node node)
{ {
string value = node.as!string; string value = node.as!string;
if(value.length != 6) if(value.length != 6)
{ {
throw new ConstructorException("Invalid color: " ~ value, start, end); throw new Exception("Invalid color: " ~ value);
} }
//We don't need to check for uppercase chars this way. //We don't need to check for uppercase chars this way.
value = value.toLower(); value = value.toLower();
@ -26,7 +26,7 @@ Color constructColorScalar(Mark start, Mark end, ref Node node)
{ {
if(!std.ascii.isHexDigit(c)) if(!std.ascii.isHexDigit(c))
{ {
throw new ConstructorException("Invalid color: " ~ value, start, end); throw new Exception("Invalid color: " ~ value);
} }
if(std.ascii.isDigit(c)) if(std.ascii.isDigit(c))
@ -44,21 +44,16 @@ Color constructColorScalar(Mark start, Mark end, ref Node node)
return result; return result;
} }
Color constructColorMapping(Mark start, Mark end, ref Node node) Color constructColorMapping(ref Node node)
{ {
ubyte r,g,b; ubyte r,g,b;
//Might throw if a value is missing is not an integer, or is out of range. //Might throw if a value is missing is not an integer, or is out of range.
try //If this happens, D:YAML will handle the exception and use its message
{ //in a YAMLException thrown when loading.
r = node["r"].as!ubyte; r = node["r"].as!ubyte;
g = node["g"].as!ubyte; g = node["g"].as!ubyte;
b = node["b"].as!ubyte; b = node["b"].as!ubyte;
}
catch(NodeException e)
{
throw new ConstructorException("Invalid color: " ~ e.msg, start, end);
}
return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b); return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b);
} }

View file

@ -10,13 +10,13 @@ struct Color
ubyte blue; ubyte blue;
} }
Color constructColorScalar(Mark start, Mark end, ref Node node) Color constructColorScalar(ref Node node)
{ {
string value = node.as!string; string value = node.as!string;
if(value.length != 6) if(value.length != 6)
{ {
throw new ConstructorException("Invalid color: " ~ value, start, end); throw new Exception("Invalid color: " ~ value);
} }
//We don't need to check for uppercase chars this way. //We don't need to check for uppercase chars this way.
value = value.toLower(); value = value.toLower();
@ -26,7 +26,7 @@ Color constructColorScalar(Mark start, Mark end, ref Node node)
{ {
if(!std.ascii.isHexDigit(c)) if(!std.ascii.isHexDigit(c))
{ {
throw new ConstructorException("Invalid color: " ~ value, start, end); throw new Exception("Invalid color: " ~ value);
} }
if(std.ascii.isDigit(c)) if(std.ascii.isDigit(c))
@ -44,21 +44,16 @@ Color constructColorScalar(Mark start, Mark end, ref Node node)
return result; return result;
} }
Color constructColorMapping(Mark start, Mark end, ref Node node) Color constructColorMapping(ref Node node)
{ {
ubyte r,g,b; ubyte r,g,b;
//Might throw if a value is missing is not an integer, or is out of range. //Might throw if a value is missing is not an integer, or is out of range.
try //If this happens, D:YAML will handle the exception and use its message
{ //in a YAMLException thrown when loading.
r = node["r"].as!ubyte; r = node["r"].as!ubyte;
g = node["g"].as!ubyte; g = node["g"].as!ubyte;
b = node["b"].as!ubyte; b = node["b"].as!ubyte;
}
catch(NodeException e)
{
throw new ConstructorException("Invalid color: " ~ e.msg, start, end);
}
return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b); return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b);
} }

View file

@ -337,17 +337,9 @@ struct TestStruct
} }
///Constructor function for TestClass. ///Constructor function for TestClass.
TestClass constructClass(Mark start, Mark end, ref Node node) TestClass constructClass(ref Node node)
{ {
try return new TestClass(node["x"].as!int, node["y"].as!int, node["z"].as!int);
{
return new TestClass(node["x"].as!int, node["y"].as!int, node["z"].as!int);
}
catch(NodeException e)
{
throw new ConstructorException("Error constructing TestClass (missing data members?) "
~ e.msg, start, end);
}
} }
Node representClass(ref Node node, Representer representer) Node representClass(ref Node node, Representer representer)
@ -362,7 +354,7 @@ Node representClass(ref Node node, Representer representer)
} }
///Constructor function for TestStruct. ///Constructor function for TestStruct.
TestStruct constructStruct(Mark start, Mark end, ref Node node) TestStruct constructStruct(ref Node node)
{ {
return TestStruct(to!int(node.as!string)); return TestStruct(to!int(node.as!string));
} }