Red-Black Trees are now used for duplicate detection, and planned

to be used for unordered map storage. This is because AAs still
don't work correctly and even if they did, require the user to
define both toHash and opCmp/opEquals for every YAML
struct/class. Now only opCmp needs to be defined.
Documentation/tutorials/examples have been updated accordingly.
This commit is contained in:
Ferdinand Majerech 2012-01-23 15:57:26 +01:00
parent 07eadc9403
commit 9596806644
34 changed files with 623 additions and 250 deletions

View file

@ -294,7 +294,7 @@ default_css =\
" border: 0.6em solid #cccccc;\n" " border: 0.6em solid #cccccc;\n"
" background-color: #f6f6f6;\n" " background-color: #f6f6f6;\n"
" font-size: 0.875em;\n" " font-size: 0.875em;\n"
" line-height: 1.4em;\n" " line-height: 1.3em;\n"
"}\n" "}\n"
"\n" "\n"
"div#content li{padding-bottom: .7ex;}\n" "div#content li{padding-bottom: .7ex;}\n"

Binary file not shown.

View file

@ -3,7 +3,7 @@ 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. YAML also you could use plain nodes such as mappings with classes' fields. YAML also
supports custom types with identifiers called *tags*. That is the topic of this supports custom types with identifiers called *tags*. That is the topic of this
tutorial. tutorial.
@ -23,11 +23,13 @@ functions to process each supported tag. These are supplied by the user using
the *addConstructorXXX()* methods, where *XXX* is *Scalar*, *Sequence* or 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 Structs and classes must implement the *opCmp()* operator for YAML support. This
define the *opEquals()* operator - this is used in equality comparisons of is used for duplicate detection in mappings, sorting and equality comparisons of
nodes. Default class *opEquals()* compares references, which means two identical nodes. The signature of the operator that must be implemented is
objects might be considered unequal. (Default struct *opEquals()* compares ``const int opCmp(ref const MyStruct s)`` for structs where *MyStruct* is the
byte-by-byte, sometimes you might want to override that as well.) struct type, and ``int opCmp(Object o)`` for classes. Note that the class
*opCmp()* should not alter the compared values - it is not const for compatibility
reasons.
We will implement support for an RGB color type. It is implemented as the We will implement support for an RGB color type. It is implemented as the
following struct: following struct:
@ -39,6 +41,14 @@ following struct:
ubyte red; ubyte red;
ubyte green; ubyte green;
ubyte blue; ubyte blue;
const int opCmp(ref const Color c)
{
if(red != c.red) {return red - c.red;}
if(green != c.green){return green - c.green;}
if(blue != c.blue) {return blue - c.blue;}
return 0;
}
} }
First, we need a function to construct our data type. The function will take a First, we need a function to construct our data type. The function will take a

View file

@ -43,7 +43,7 @@ Do this by typing the following command into the console::
dmd cdc.d dmd cdc.d
Now you can use CDC to compile D:YAML. Now compile D:YAML with CDC.
To do this on Unix/Linux, use the following command:: To do this on Unix/Linux, use the following command::
./cdc ./cdc
@ -101,8 +101,8 @@ into the file:
Explanation of the code Explanation of the code
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
First, we import the *yaml* module. This is the only module you need to import First, we import the *yaml* module. This is the only D:YAML module you need to
to use D:YAML - it automatically imports all needed modules. import - it automatically imports all needed modules.
Next we load the file using the *Loader.load()* method. *Loader* is a struct Next we load the file using the *Loader.load()* method. *Loader* is a struct
used for parsing YAML documents. The *load()* method loads the file as used for parsing YAML documents. The *load()* method loads the file as
@ -146,18 +146,18 @@ formatted differently. Comments are not preserved, either.
Compiling Compiling
^^^^^^^^^ ^^^^^^^^^
To compile your project, you must give DMD the directories containing import To compile your project, DMD needs to know which directories contain the
modules and the library. You also need to tell it to link with D:YAML. The import imported modules and the library. You also need to tell it to link with D:YAML.
directory should be the D:YAML package directory. You can specify it using the The import directory should be the D:YAML package directory. You can specify it
``-I`` option of DMD. The library directory should be where you put the compiled using the ``-I`` option of DMD. The library directory should point to the
D:YAML library. On Unix/Linux you can specify it using the ``-L-L`` option, and compiled library. On Unix/Linux you can specify it using the ``-L-L`` option,
link with D:YAML using the ``-L-l`` option. On Windows, the import directory is and link with D:YAML using the ``-L-l`` option. On Windows, the import directory
used as the library directory. To link with the library on Windows, just add the is used as the library directory. To link with the library on Windows, just add
path to it relative to the current directory. the path to it relative to the current directory.
For example, if you extracted and compiled D:YAML in ``/home/xxx/dyaml``, your For example, if you extracted and compiled D:YAML in ``/home/xxx/dyaml``, your
project is in ``/home/xxx/dyaml-project``, and you are currently in that project is in ``/home/xxx/dyaml-project``, and you are currently in that
directory, you can compile the project with the following command on Unix/Linux:: directory, compile the project with the following command on Unix/Linux::
dmd -I../dyaml -L-L../dyaml -L-ldyaml main.d dmd -I../dyaml -L-L../dyaml -L-ldyaml main.d

View file

@ -190,7 +190,7 @@ div#content
border: 0.6em solid #cccccc; border: 0.6em solid #cccccc;
background-color: #f6f6f6; background-color: #f6f6f6;
font-size: 0.875em; font-size: 0.875em;
line-height: 1.4em; line-height: 1.3em;
} }
div#content li{padding-bottom: .7ex;} div#content li{padding-bottom: .7ex;}

View file

@ -72,6 +72,8 @@
<p>Each YAML scalar, sequence or mapping has a tag specifying its data type. <p>Each YAML scalar, sequence or mapping has a tag specifying its data type.
<a name="Constructor"></a><span class="ddoc_psymbol">Constructor</span> uses user-specifyable functions to create a node of desired <a name="Constructor"></a><span class="ddoc_psymbol">Constructor</span> uses user-specifyable functions to create a node of desired
data type from a scalar, sequence or mapping. data type from a scalar, sequence or mapping.
<br>
<br> <br>
Each of these functions is associated with a tag, and can process either Each of these functions is associated with a tag, and can process either
@ -82,7 +84,7 @@
If a tag is detected with no known constructor function, it is considered an error.</p> If a tag is detected with no known constructor function, it is considered an error.</p>
<dl><dt class="d_decl">this(in const(bool) <b>defaultConstructors</b> = true); <dl><dt class="d_decl">this(const(bool) <b>defaultConstructors</b> = true);
</dt> </dt>
<dd><p>Construct a Constructor. <dd><p>Construct a Constructor.
</p> </p>
@ -107,12 +109,24 @@
Any exception thrown by this function will be caught by D:YAML and 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 its message will be added to a YAMLException that will also tell the
user which type failed to construct, and position in the file. user which type failed to construct, and position in the file.
<br>
<br> <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>
Only one constructor function can be set for one tag. Only one constructor function can be set for one tag.
<br>
<br>
Structs and classes must implement the <span class="d_inlinecode">opCmp()</span> operator for D:YAML
support. The signature of the operator that must be implemented
is <span class="d_inlinecode">const int opCmp(ref const MyStruct s)</span> for structs where
<i>MyStruct</i> is the struct type, and <span class="d_inlinecode">int opCmp(Object o)</span> for
classes. Note that the class <span class="d_inlinecode">opCmp()</span> should not alter the compared
values - it is not const for compatibility reasons.
</p> </p>
<b>Parameters:</b><div class="pbr"><table class=parms><tr><td valign=top>tag</td> <b>Parameters:</b><div class="pbr"><table class=parms><tr><td valign=top>tag</td>
@ -128,6 +142,16 @@
<span class="d_keyword">struct</span> MyStruct <span class="d_keyword">struct</span> MyStruct
{ {
<span class="d_keyword">int</span> x, y, z; <span class="d_keyword">int</span> x, y, z;
<span class="d_comment">//Any D:YAML type must have a custom opCmp operator.
</span> <span class="d_comment">//This is used for ordering in mappings.
</span> <span class="d_keyword">const</span> <span class="d_keyword">int</span> opCmp(<span class="d_keyword">ref</span> <span class="d_keyword">const</span> MyStruct s)
{
<span class="d_keyword">if</span>(x != s.x){<span class="d_keyword">return</span> x - s.x;}
<span class="d_keyword">if</span>(y != s.y){<span class="d_keyword">return</span> y - s.y;}
<span class="d_keyword">if</span>(z != s.z){<span class="d_keyword">return</span> z - s.z;}
<span class="d_keyword">return</span> 0;
}
} }
MyStruct constructMyStructScalar(<span class="d_keyword">ref</span> Node node) MyStruct constructMyStructScalar(<span class="d_keyword">ref</span> Node node)
@ -166,6 +190,16 @@
<span class="d_keyword">struct</span> MyStruct <span class="d_keyword">struct</span> MyStruct
{ {
<span class="d_keyword">int</span> x, y, z; <span class="d_keyword">int</span> x, y, z;
<span class="d_comment">//Any D:YAML type must have a custom opCmp operator.
</span> <span class="d_comment">//This is used for ordering in mappings.
</span> <span class="d_keyword">const</span> <span class="d_keyword">int</span> opCmp(<span class="d_keyword">ref</span> <span class="d_keyword">const</span> MyStruct s)
{
<span class="d_keyword">if</span>(x != s.x){<span class="d_keyword">return</span> x - s.x;}
<span class="d_keyword">if</span>(y != s.y){<span class="d_keyword">return</span> y - s.y;}
<span class="d_keyword">if</span>(z != s.z){<span class="d_keyword">return</span> z - s.z;}
<span class="d_keyword">return</span> 0;
}
} }
MyStruct constructMyStructSequence(<span class="d_keyword">ref</span> Node node) MyStruct constructMyStructSequence(<span class="d_keyword">ref</span> Node node)
@ -202,6 +236,16 @@
<span class="d_keyword">struct</span> MyStruct <span class="d_keyword">struct</span> MyStruct
{ {
<span class="d_keyword">int</span> x, y, z; <span class="d_keyword">int</span> x, y, z;
<span class="d_comment">//Any D:YAML type must have a custom opCmp operator.
</span> <span class="d_comment">//This is used for ordering in mappings.
</span> <span class="d_keyword">const</span> <span class="d_keyword">int</span> opCmp(<span class="d_keyword">ref</span> <span class="d_keyword">const</span> MyStruct s)
{
<span class="d_keyword">if</span>(x != s.x){<span class="d_keyword">return</span> x - s.x;}
<span class="d_keyword">if</span>(y != s.y){<span class="d_keyword">return</span> y - s.y;}
<span class="d_keyword">if</span>(z != s.z){<span class="d_keyword">return</span> z - s.z;}
<span class="d_keyword">return</span> 0;
}
} }
MyStruct constructMyStructMapping(<span class="d_keyword">ref</span> Node node) MyStruct constructMyStructMapping(<span class="d_keyword">ref</span> Node node)
@ -227,42 +271,42 @@
</dd> </dd>
<dt class="d_decl">YAMLNull <a name="constructNull"></a><span class="ddoc_psymbol">constructNull</span>(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 null node.</p>
</dd> </dd>
<dt class="d_decl">YAMLMerge <a name="constructMerge"></a><span class="ddoc_psymbol">constructMerge</span>(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 node - a node that merges another node into a mapping.</p>
</dd> </dd>
<dt class="d_decl">bool <a name="constructBool"></a><span class="ddoc_psymbol">constructBool</span>(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 node.</p>
</dd> </dd>
<dt class="d_decl">long <a name="constructLong"></a><span class="ddoc_psymbol">constructLong</span>(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) node.</p>
</dd> </dd>
<dt class="d_decl">real <a name="constructReal"></a><span class="ddoc_psymbol">constructReal</span>(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) node.</p>
</dd> </dd>
<dt class="d_decl">ubyte[] <a name="constructBinary"></a><span class="ddoc_psymbol">constructBinary</span>(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) node.</p>
</dd> </dd>
<dt class="d_decl">SysTime <a name="constructTimestamp"></a><span class="ddoc_psymbol">constructTimestamp</span>(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) node.</p>
</dd> </dd>
<dt class="d_decl">string <a name="constructString"></a><span class="ddoc_psymbol">constructString</span>(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 node.</p>
</dd> </dd>
<dt class="d_decl">Pair[] <a name="getPairs"></a><span class="ddoc_psymbol">getPairs</span>(string <b>type</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>);
@ -272,27 +316,27 @@
</dd> </dd>
<dt class="d_decl">Pair[] <a name="constructOrderedMap"></a><span class="ddoc_psymbol">constructOrderedMap</span>(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) node.</p>
</dd> </dd>
<dt class="d_decl">Pair[] <a name="constructPairs"></a><span class="ddoc_psymbol">constructPairs</span>(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) node.</p>
</dd> </dd>
<dt class="d_decl">Node[] <a name="constructSet"></a><span class="ddoc_psymbol">constructSet</span>(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 node.</p>
</dd> </dd>
<dt class="d_decl">Node[] <a name="constructSequence"></a><span class="ddoc_psymbol">constructSequence</span>(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) node.</p>
</dd> </dd>
<dt class="d_decl">Pair[] <a name="constructMap"></a><span class="ddoc_psymbol">constructMap</span>(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) node.</p>
</dd> </dd>
</dl> </dl>
@ -301,7 +345,7 @@
<div id="copyright"> <div id="copyright">
Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. | Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. |
Page generated by Autodoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>. Page generated by AutoDDoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>.
</div> </div>
</body> </body>

View file

@ -224,7 +224,7 @@
<div id="copyright"> <div id="copyright">
Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. | Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. |
Page generated by Autodoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>. Page generated by AutoDDoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>.
</div> </div>
</body> </body>

View file

@ -65,7 +65,7 @@
<div id="copyright"> <div id="copyright">
Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. | Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. |
Page generated by Autodoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>. Page generated by AutoDDoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>.
</div> </div>
</body> </body>

View file

@ -57,7 +57,7 @@
</dt> </dt>
<dd><p>Position in a YAML stream, used for error messages.</p> <dd><p>Position in a YAML stream, used for error messages.</p>
<dl><dt class="d_decl">this(in const(uint) <b>line</b>, in const(uint) <b>column</b>); <dl><dt class="d_decl">this(const(uint) <b>line</b>, const(uint) <b>column</b>);
</dt> </dt>
<dd><p>Construct a Mark with specified <b>line</b> and <b>column</b> in the file.</p> <dd><p>Construct a Mark with specified <b>line</b> and <b>column</b> in the file.</p>
@ -75,7 +75,7 @@
<div id="copyright"> <div id="copyright">
Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. | Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. |
Page generated by Autodoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>. Page generated by AutoDDoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>.
</div> </div>
</body> </body>

View file

@ -65,7 +65,7 @@
<div id="copyright"> <div id="copyright">
Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. | Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. |
Page generated by Autodoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>. Page generated by AutoDDoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>.
</div> </div>
</body> </body>

View file

@ -190,7 +190,7 @@
<div id="copyright"> <div id="copyright">
Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. | Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. |
Page generated by Autodoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>. Page generated by AutoDDoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>.
</div> </div>
</body> </body>

View file

@ -400,7 +400,7 @@
</dd> </dd>
<dt class="d_decl">int <a name="opApply"></a><span class="ddoc_psymbol">opApply</span>(T)(int delegate(ref T) <b>dg</b>); <dt class="d_decl">int <a name="opApply"></a><span class="ddoc_psymbol">opApply</span>(T)(int delegate(ref T) <b>dg</b>);
</dt> </dt>
<dd><p>Iterate over a sequence, getting each element as T. <dd><p>Foreach over a sequence, getting each element as T.
</p> </p>
<p>If T is Node, simply iterate over the nodes in the sequence. <p>If T is Node, simply iterate over the nodes in the sequence.
Otherwise, convert each node to T during iteration. Otherwise, convert each node to T during iteration.
@ -412,7 +412,7 @@
</dd> </dd>
<dt class="d_decl">int <a name="opApply"></a><span class="ddoc_psymbol">opApply</span>(K, V)(int delegate(ref K, ref V) <b>dg</b>); <dt class="d_decl">int <a name="opApply"></a><span class="ddoc_psymbol">opApply</span>(K, V)(int delegate(ref K, ref V) <b>dg</b>);
</dt> </dt>
<dd><p>Iterate over a mapping, getting each key/value as K/V. <dd><p>Foreach over a mapping, getting each key/value as K/V.
</p> </p>
<p>If the K and/or V is Node, simply iterate over the nodes in the mapping. <p>If the K and/or V is Node, simply iterate over the nodes in the mapping.
Otherwise, convert each key/value to T during iteration. Otherwise, convert each key/value to T during iteration.
@ -512,6 +512,11 @@
<b>Throws:</b><div class="pbr">NodeException if the node is not a collection, index is out <b>Throws:</b><div class="pbr">NodeException if the node is not a collection, index is out
of range or if a non-integral index is used on a sequence node.</div> of range or if a non-integral index is used on a sequence node.</div>
</dd>
<dt class="d_decl">const const int <a name="opCmp"></a><span class="ddoc_psymbol">opCmp</span>(ref const Node <b>node</b>);
</dt>
<dd><p>Compare with another node.</p>
</dd> </dd>
</dl> </dl>
</dd> </dd>
@ -521,7 +526,7 @@
<div id="copyright"> <div id="copyright">
Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. | Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. |
Page generated by Autodoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>. Page generated by AutoDDoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>.
</div> </div>
</body> </body>

View file

@ -52,7 +52,7 @@
</dd> </dd>
<dt class="d_decl">class <a name="Representer"></a><span class="ddoc_psymbol">Representer</span>; <dt class="d_decl">class <a name="Representer"></a><span class="ddoc_psymbol">Representer</span>;
</dt> </dt>
<dd><p>Represents YAML nodes of various data types as scalar, sequence and mapping nodes ready for output. <dd><p>Represents YAML nodes as scalar, sequence and mapping nodes ready for output.
</p> </p>
<p>This class is used to add support for dumping of custom data types. <p>This class is used to add support for dumping of custom data types.
<br> <br>
@ -73,26 +73,38 @@
</dd> </dd>
<dt class="d_decl">@property void <a name="defaultScalarStyle"></a><span class="ddoc_psymbol">defaultScalarStyle</span>(ScalarStyle <b>style</b>); <dt class="d_decl">@property void <a name="defaultScalarStyle"></a><span class="ddoc_psymbol">defaultScalarStyle</span>(ScalarStyle <b>style</b>);
</dt> </dt>
<dd><p>Set default style for scalars. Invalid means the style is chosen automatically.</p> <dd><p>Set default style for scalars. If <b>style</b> is <span class="d_inlinecode">ScalarStyle.Invalid</span>, the style is chosen automatically.</p>
</dd> </dd>
<dt class="d_decl">@property void <a name="defaultCollectionStyle"></a><span class="ddoc_psymbol">defaultCollectionStyle</span>(CollectionStyle <b>style</b>); <dt class="d_decl">@property void <a name="defaultCollectionStyle"></a><span class="ddoc_psymbol">defaultCollectionStyle</span>(CollectionStyle <b>style</b>);
</dt> </dt>
<dd><p>Set default style for collections. Invalid means the style is chosen automatically. </p> <dd><p>Set default style for collections. If <b>style</b> is <span class="d_inlinecode">CollectionStyle.Invalid</span>, the style is chosen automatically. </p>
</dd> </dd>
<dt class="d_decl">void <a name="addRepresenter"></a><span class="ddoc_psymbol">addRepresenter</span>(T)(Node function(ref Node, Representer) <b>representer</b>); <dt class="d_decl">void <a name="addRepresenter"></a><span class="ddoc_psymbol">addRepresenter</span>(T)(Node function(ref Node, Representer) <b>representer</b>);
</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 references to a Node storing the data <p>The representer function takes references to a <span class="d_inlinecode">Node</span> storing the data
type and to the Representer. It returns the represented node and may type and to the <span class="d_inlinecode">Representer</span>. It returns the represented node and may
throw a RepresenterException. See the example for more information. throw a <span class="d_inlinecode">RepresenterException</span>. See the example for more information.
<br>
<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 the types already have representer functions unless disabled in the
Representer constructor. <span class="d_inlinecode">Representer</span> constructor.
<br>
<br>
Structs and classes must implement the <span class="d_inlinecode">opCmp()</span> operator for D:YAML
support. The signature of the operator that must be implemented
is <span class="d_inlinecode">const int opCmp(ref const MyStruct s)</span> for structs where
<i>MyStruct</i> is the struct type, and <span class="d_inlinecode">int opCmp(Object o)</span> for
classes. Note that the class <span class="d_inlinecode">opCmp()</span> should not alter the compared
values - it is not const for compatibility reasons.
</p> </p>
<b>Parameters:</b><div class="pbr"><table class=parms><tr><td valign=top>representer</td> <b>Parameters:</b><div class="pbr"><table class=parms><tr><td valign=top>representer</td>
@ -106,6 +118,16 @@
<span class="d_keyword">struct</span> MyStruct <span class="d_keyword">struct</span> MyStruct
{ {
<span class="d_keyword">int</span> x, y, z; <span class="d_keyword">int</span> x, y, z;
<span class="d_comment">//Any D:YAML type must have a custom opCmp operator.
</span> <span class="d_comment">//This is used for ordering in mappings.
</span> <span class="d_keyword">const</span> <span class="d_keyword">int</span> opCmp(<span class="d_keyword">ref</span> <span class="d_keyword">const</span> MyStruct s)
{
<span class="d_keyword">if</span>(x != s.x){<span class="d_keyword">return</span> x - s.x;}
<span class="d_keyword">if</span>(y != s.y){<span class="d_keyword">return</span> y - s.y;}
<span class="d_keyword">if</span>(z != s.z){<span class="d_keyword">return</span> z - s.z;}
<span class="d_keyword">return</span> 0;
}
} }
Node representMyStruct(<span class="d_keyword">ref</span> Node node, Representer representer) Node representMyStruct(<span class="d_keyword">ref</span> Node node, Representer representer)
@ -144,12 +166,16 @@
<span class="d_keyword">this</span>.z = z; <span class="d_keyword">this</span>.z = z;
} }
<span class="d_comment">///We need custom opEquals for node equality, as default opEquals compares references. <span class="d_comment">//Any D:YAML type must have a custom opCmp operator.
</span> <span class="d_keyword">override</span> <span class="d_keyword">bool</span> opEquals(Object rhs) </span> <span class="d_comment">//This is used for ordering in mappings.
</span> <span class="d_keyword">override</span> <span class="d_keyword">int</span> opCmp(Object o)
{ {
<span class="d_keyword">if</span>(<span class="d_keyword">typeid</span>(rhs) != <span class="d_keyword">typeid</span>(MyClass)){<span class="d_keyword">return</span> <span class="d_keyword">false</span>;} MyClass s = <span class="d_keyword">cast</span>(MyClass)o;
<span class="d_keyword">auto</span> t = <span class="d_keyword">cast</span>(MyClass)rhs; <span class="d_keyword">if</span>(s <span class="d_keyword">is</span> <span class="d_keyword">null</span>){<span class="d_keyword">return</span> -1;}
<span class="d_keyword">return</span> x == t.x &amp;&amp; y == t.y &amp;&amp; z == t.z; <span class="d_keyword">if</span>(x != s.x){<span class="d_keyword">return</span> x - s.x;}
<span class="d_keyword">if</span>(y != s.y){<span class="d_keyword">return</span> y - s.y;}
<span class="d_keyword">if</span>(z != s.z){<span class="d_keyword">return</span> z - s.z;}
<span class="d_keyword">return</span> 0;
} }
<span class="d_comment">///Useful for Node.as!string . <span class="d_comment">///Useful for Node.as!string .
@ -204,6 +230,16 @@
<pre class="d_code"> <span class="d_keyword">struct</span> MyStruct <pre class="d_code"> <span class="d_keyword">struct</span> MyStruct
{ {
<span class="d_keyword">int</span> x, y, z; <span class="d_keyword">int</span> x, y, z;
<span class="d_comment">//Any D:YAML type must have a custom opCmp operator.
</span> <span class="d_comment">//This is used for ordering in mappings.
</span> <span class="d_keyword">const</span> <span class="d_keyword">int</span> opCmp(<span class="d_keyword">ref</span> <span class="d_keyword">const</span> MyStruct s)
{
<span class="d_keyword">if</span>(x != s.x){<span class="d_keyword">return</span> x - s.x;}
<span class="d_keyword">if</span>(y != s.y){<span class="d_keyword">return</span> y - s.y;}
<span class="d_keyword">if</span>(z != s.z){<span class="d_keyword">return</span> z - s.z;}
<span class="d_keyword">return</span> 0;
}
} }
Node representMyStruct(<span class="d_keyword">ref</span> Node node, Representer representer) Node representMyStruct(<span class="d_keyword">ref</span> Node node, Representer representer)
@ -211,7 +247,6 @@
<span class="d_keyword">auto</span> value = node.as!MyStruct; <span class="d_keyword">auto</span> value = node.as!MyStruct;
<span class="d_keyword">auto</span> <span class="d_param">scalar</span> = format(value.x, <span class="d_string">":"</span>, value.y, <span class="d_string">":"</span>, value.z); <span class="d_keyword">auto</span> <span class="d_param">scalar</span> = format(value.x, <span class="d_string">":"</span>, value.y, <span class="d_string">":"</span>, value.z);
<span class="d_keyword">return</span> representer.<span class="d_psymbol">representScalar</span>(<span class="d_string">"!mystruct.tag"</span>, <span class="d_param">scalar</span>); <span class="d_keyword">return</span> representer.<span class="d_psymbol">representScalar</span>(<span class="d_string">"!mystruct.tag"</span>, <span class="d_param">scalar</span>);
} }
</pre> </pre>
</p> </p>
@ -225,7 +260,7 @@
</p> </p>
<b>Parameters:</b><div class="pbr"><table class=parms><tr><td valign=top>string <b>tag</b></td> <b>Parameters:</b><div class="pbr"><table class=parms><tr><td valign=top>string <b>tag</b></td>
<td valign=top>Tag of the <b>sequence</b>.</td></tr> <td valign=top>Tag of the sequence.</td></tr>
<tr><td valign=top>Node[] <b>sequence</b></td> <tr><td valign=top>Node[] <b>sequence</b></td>
<td valign=top>Sequence of nodes.</td></tr> <td valign=top>Sequence of nodes.</td></tr>
<tr><td valign=top>CollectionStyle <b>style</b></td> <tr><td valign=top>CollectionStyle <b>style</b></td>
@ -235,13 +270,23 @@
<b>Returns:</b><div class="pbr">The represented node. <b>Returns:</b><div class="pbr">The represented node.
</div> </div>
<b>Throws:</b><div class="pbr">RepresenterException if a child could not be represented. <b>Throws:</b><div class="pbr"><span class="d_inlinecode">RepresenterException</span> if a child could not be represented.
</div> </div>
<p><b>Example:</b><br> <p><b>Example:</b><br>
<pre class="d_code"> <span class="d_keyword">struct</span> MyStruct <pre class="d_code"> <span class="d_keyword">struct</span> MyStruct
{ {
<span class="d_keyword">int</span> x, y, z; <span class="d_keyword">int</span> x, y, z;
<span class="d_comment">//Any D:YAML type must have a custom opCmp operator.
</span> <span class="d_comment">//This is used for ordering in mappings.
</span> <span class="d_keyword">const</span> <span class="d_keyword">int</span> opCmp(<span class="d_keyword">ref</span> <span class="d_keyword">const</span> MyStruct s)
{
<span class="d_keyword">if</span>(x != s.x){<span class="d_keyword">return</span> x - s.x;}
<span class="d_keyword">if</span>(y != s.y){<span class="d_keyword">return</span> y - s.y;}
<span class="d_keyword">if</span>(z != s.z){<span class="d_keyword">return</span> z - s.z;}
<span class="d_keyword">return</span> 0;
}
} }
Node representMyStruct(<span class="d_keyword">ref</span> Node node, Representer representer) Node representMyStruct(<span class="d_keyword">ref</span> Node node, Representer representer)
@ -274,13 +319,23 @@
<b>Returns:</b><div class="pbr">The represented node. <b>Returns:</b><div class="pbr">The represented node.
</div> </div>
<b>Throws:</b><div class="pbr">RepresenterException if a child could not be represented. <b>Throws:</b><div class="pbr"><span class="d_inlinecode">RepresenterException</span> if a child could not be represented.
</div> </div>
<p><b>Example:</b><br> <p><b>Example:</b><br>
<pre class="d_code"> <span class="d_keyword">struct</span> MyStruct <pre class="d_code"> <span class="d_keyword">struct</span> MyStruct
{ {
<span class="d_keyword">int</span> x, y, z; <span class="d_keyword">int</span> x, y, z;
<span class="d_comment">//Any D:YAML type must have a custom opCmp operator.
</span> <span class="d_comment">//This is used for ordering in mappings.
</span> <span class="d_keyword">const</span> <span class="d_keyword">int</span> opCmp(<span class="d_keyword">ref</span> <span class="d_keyword">const</span> MyStruct s)
{
<span class="d_keyword">if</span>(x != s.x){<span class="d_keyword">return</span> x - s.x;}
<span class="d_keyword">if</span>(y != s.y){<span class="d_keyword">return</span> y - s.y;}
<span class="d_keyword">if</span>(z != s.z){<span class="d_keyword">return</span> z - s.z;}
<span class="d_keyword">return</span> 0;
}
} }
Node representMyStruct(<span class="d_keyword">ref</span> Node node, Representer representer) Node representMyStruct(<span class="d_keyword">ref</span> Node node, Representer representer)
@ -348,7 +403,7 @@
<div id="copyright"> <div id="copyright">
Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. | Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. |
Page generated by Autodoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>. Page generated by AutoDDoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>.
</div> </div>
</body> </body>

View file

@ -133,7 +133,7 @@
<div id="copyright"> <div id="copyright">
Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. | Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. |
Page generated by Autodoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>. Page generated by AutoDDoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>.
</div> </div>
</body> </body>

View file

@ -96,7 +96,7 @@
<div id="copyright"> <div id="copyright">
Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. | Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. |
Page generated by Autodoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>. Page generated by AutoDDoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>.
</div> </div>
</body> </body>

View file

@ -59,7 +59,7 @@ import the <i>yaml</i> module, which will import all needed modules.
<div id="copyright"> <div id="copyright">
Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. | Copyright &copy; Ferdinand Majerech 2011. Based on <a href="http://www.pyyaml.org">PyYAML</a> by Kirill Simonov. |
Page generated by Autodoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>. Page generated by AutoDDoc and <a href="http://www.digitalmars.com/d/2.0/ddoc.html">Ddoc</a>.
</div> </div>
</body> </body>

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 18, 2011. Last updated on Jan 23, 2012.
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 18, 2011. Last updated on Jan 23, 2012.
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 18, 2011. Last updated on Jan 23, 2012.
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,7 +48,7 @@
<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. YAML also you could use plain nodes such as mappings with classes&#8217; fields. YAML also
supports custom types with identifiers called <em>tags</em>. That is the topic of this supports custom types with identifiers called <em>tags</em>. That is the topic of 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
@ -62,11 +62,13 @@ each node to hold data type corresponding to its tag. <em>Constructor</em> store
functions to process each supported tag. These are supplied by the user using 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>Structs and classes must implement the <em>opCmp()</em> operator for YAML support. This
define the <em>opEquals()</em> operator - this is used in equality comparisons of is used for duplicate detection in mappings, sorting and equality comparisons of
nodes. Default class <em>opEquals()</em> compares references, which means two identical nodes. The signature of the operator that must be implemented is
objects might be considered unequal. (Default struct <em>opEquals()</em> compares <tt class="docutils literal"><span class="pre">const</span> <span class="pre">int</span> <span class="pre">opCmp(ref</span> <span class="pre">const</span> <span class="pre">MyStruct</span> <span class="pre">s)</span></tt> for structs where <em>MyStruct</em> is the
byte-by-byte, sometimes you might want to override that as well.)</p> struct type, and <tt class="docutils literal"><span class="pre">int</span> <span class="pre">opCmp(Object</span> <span class="pre">o)</span></tt> for classes. Note that the class
<em>opCmp()</em> should not alter the compared values - it is not const for compatibility
reasons.</p>
<p>We will implement support for an RGB color type. It is implemented as the <p>We will implement support for an RGB color type. It is implemented as the
following struct:</p> following struct:</p>
<div class="highlight-d"><div class="highlight"><pre><span class="k">struct</span> <span class="n">Color</span> <div class="highlight-d"><div class="highlight"><pre><span class="k">struct</span> <span class="n">Color</span>
@ -74,6 +76,14 @@ following struct:</p>
<span class="kt">ubyte</span> <span class="n">red</span><span class="p">;</span> <span class="kt">ubyte</span> <span class="n">red</span><span class="p">;</span>
<span class="kt">ubyte</span> <span class="n">green</span><span class="p">;</span> <span class="kt">ubyte</span> <span class="n">green</span><span class="p">;</span>
<span class="kt">ubyte</span> <span class="n">blue</span><span class="p">;</span> <span class="kt">ubyte</span> <span class="n">blue</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">opCmp</span><span class="p">(</span><span class="k">ref</span> <span class="k">const</span> <span class="n">Color</span> <span class="n">c</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span><span class="p">(</span><span class="n">red</span> <span class="p">!=</span> <span class="n">c</span><span class="p">.</span><span class="n">red</span><span class="p">)</span> <span class="p">{</span><span class="k">return</span> <span class="n">red</span> <span class="p">-</span> <span class="n">c</span><span class="p">.</span><span class="n">red</span><span class="p">;}</span>
<span class="k">if</span><span class="p">(</span><span class="n">green</span> <span class="p">!=</span> <span class="n">c</span><span class="p">.</span><span class="n">green</span><span class="p">){</span><span class="k">return</span> <span class="n">green</span> <span class="p">-</span> <span class="n">c</span><span class="p">.</span><span class="n">green</span><span class="p">;}</span>
<span class="k">if</span><span class="p">(</span><span class="n">blue</span> <span class="p">!=</span> <span class="n">c</span><span class="p">.</span><span class="n">blue</span><span class="p">)</span> <span class="p">{</span><span class="k">return</span> <span class="n">blue</span> <span class="p">-</span> <span class="n">c</span><span class="p">.</span><span class="n">blue</span><span class="p">;}</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span> <span class="p">}</span>
</pre></div> </pre></div>
</div> </div>
@ -364,7 +374,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 18, 2011. Last updated on Jan 23, 2012.
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

@ -78,7 +78,7 @@ script for compilation. To compile D:YAML, you first need to build CDC.
Do this by typing the following command into the console:</p> Do this by typing the following command into the console:</p>
<div class="highlight-python"><pre>dmd cdc.d</pre> <div class="highlight-python"><pre>dmd cdc.d</pre>
</div> </div>
<p>Now you can use CDC to compile D:YAML. <p>Now compile D:YAML with CDC.
To do this on Unix/Linux, use the following command:</p> To do this on Unix/Linux, use the following command:</p>
<div class="highlight-python"><pre>./cdc</pre> <div class="highlight-python"><pre>./cdc</pre>
</div> </div>
@ -125,8 +125,8 @@ into the file:</p>
</div> </div>
<div class="section" id="explanation-of-the-code"> <div class="section" id="explanation-of-the-code">
<h3>Explanation of the code<a class="headerlink" href="#explanation-of-the-code" title="Permalink to this headline"></a></h3> <h3>Explanation of the code<a class="headerlink" href="#explanation-of-the-code" title="Permalink to this headline"></a></h3>
<p>First, we import the <em>yaml</em> module. This is the only module you need to import <p>First, we import the <em>yaml</em> module. This is the only D:YAML module you need to
to use D:YAML - it automatically imports all needed modules.</p> import - it automatically imports all needed modules.</p>
<p>Next we load the file using the <em>Loader.load()</em> method. <em>Loader</em> is a struct <p>Next we load the file using the <em>Loader.load()</em> method. <em>Loader</em> is a struct
used for parsing YAML documents. The <em>load()</em> method loads the file as used for parsing YAML documents. The <em>load()</em> method loads the file as
<strong>one</strong> YAML document, or throws <em>YAMLException</em>, D:YAML exception type, if the <strong>one</strong> YAML document, or throws <em>YAMLException</em>, D:YAML exception type, if the
@ -160,17 +160,17 @@ formatted differently. Comments are not preserved, either.</p>
</div> </div>
<div class="section" id="compiling"> <div class="section" id="compiling">
<h3>Compiling<a class="headerlink" href="#compiling" title="Permalink to this headline"></a></h3> <h3>Compiling<a class="headerlink" href="#compiling" title="Permalink to this headline"></a></h3>
<p>To compile your project, you must give DMD the directories containing import <p>To compile your project, DMD needs to know which directories contain the
modules and the library. You also need to tell it to link with D:YAML. The import imported modules and the library. You also need to tell it to link with D:YAML.
directory should be the D:YAML package directory. You can specify it using the The import directory should be the D:YAML package directory. You can specify it
<tt class="docutils literal"><span class="pre">-I</span></tt> option of DMD. The library directory should be where you put the compiled using the <tt class="docutils literal"><span class="pre">-I</span></tt> option of DMD. The library directory should point to the
D:YAML library. On Unix/Linux you can specify it using the <tt class="docutils literal"><span class="pre">-L-L</span></tt> option, and compiled library. On Unix/Linux you can specify it using the <tt class="docutils literal"><span class="pre">-L-L</span></tt> option,
link with D:YAML using the <tt class="docutils literal"><span class="pre">-L-l</span></tt> option. On Windows, the import directory is and link with D:YAML using the <tt class="docutils literal"><span class="pre">-L-l</span></tt> option. On Windows, the import directory
used as the library directory. To link with the library on Windows, just add the is used as the library directory. To link with the library on Windows, just add
path to it relative to the current directory.</p> the path to it relative to the current directory.</p>
<p>For example, if you extracted and compiled D:YAML in <tt class="docutils literal"><span class="pre">/home/xxx/dyaml</span></tt>, your <p>For example, if you extracted and compiled D:YAML in <tt class="docutils literal"><span class="pre">/home/xxx/dyaml</span></tt>, your
project is in <tt class="docutils literal"><span class="pre">/home/xxx/dyaml-project</span></tt>, and you are currently in that project is in <tt class="docutils literal"><span class="pre">/home/xxx/dyaml-project</span></tt>, and you are currently in that
directory, you can compile the project with the following command on Unix/Linux:</p> directory, compile the project with the following command on Unix/Linux:</p>
<div class="highlight-python"><pre>dmd -I../dyaml -L-L../dyaml -L-ldyaml main.d</pre> <div class="highlight-python"><pre>dmd -I../dyaml -L-L../dyaml -L-ldyaml main.d</pre>
</div> </div>
<p>And the following on Windows:</p> <p>And the following on Windows:</p>
@ -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 18, 2011. Last updated on Jan 23, 2012.
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 18, 2011. Last updated on Jan 23, 2012.
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,7 +3,7 @@ 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. YAML also you could use plain nodes such as mappings with classes' fields. YAML also
supports custom types with identifiers called *tags*. That is the topic of this supports custom types with identifiers called *tags*. That is the topic of this
tutorial. tutorial.
@ -23,11 +23,13 @@ functions to process each supported tag. These are supplied by the user using
the *addConstructorXXX()* methods, where *XXX* is *Scalar*, *Sequence* or 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 Structs and classes must implement the *opCmp()* operator for YAML support. This
define the *opEquals()* operator - this is used in equality comparisons of is used for duplicate detection in mappings, sorting and equality comparisons of
nodes. Default class *opEquals()* compares references, which means two identical nodes. The signature of the operator that must be implemented is
objects might be considered unequal. (Default struct *opEquals()* compares ``const int opCmp(ref const MyStruct s)`` for structs where *MyStruct* is the
byte-by-byte, sometimes you might want to override that as well.) struct type, and ``int opCmp(Object o)`` for classes. Note that the class
*opCmp()* should not alter the compared values - it is not const for compatibility
reasons.
We will implement support for an RGB color type. It is implemented as the We will implement support for an RGB color type. It is implemented as the
following struct: following struct:
@ -39,6 +41,14 @@ following struct:
ubyte red; ubyte red;
ubyte green; ubyte green;
ubyte blue; ubyte blue;
const int opCmp(ref const Color c)
{
if(red != c.red) {return red - c.red;}
if(green != c.green){return green - c.green;}
if(blue != c.blue) {return blue - c.blue;}
return 0;
}
} }
First, we need a function to construct our data type. The function will take a First, we need a function to construct our data type. The function will take a

View file

@ -43,7 +43,7 @@ Do this by typing the following command into the console::
dmd cdc.d dmd cdc.d
Now you can use CDC to compile D:YAML. Now compile D:YAML with CDC.
To do this on Unix/Linux, use the following command:: To do this on Unix/Linux, use the following command::
./cdc ./cdc
@ -101,8 +101,8 @@ into the file:
Explanation of the code Explanation of the code
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
First, we import the *yaml* module. This is the only module you need to import First, we import the *yaml* module. This is the only D:YAML module you need to
to use D:YAML - it automatically imports all needed modules. import - it automatically imports all needed modules.
Next we load the file using the *Loader.load()* method. *Loader* is a struct Next we load the file using the *Loader.load()* method. *Loader* is a struct
used for parsing YAML documents. The *load()* method loads the file as used for parsing YAML documents. The *load()* method loads the file as
@ -146,18 +146,18 @@ formatted differently. Comments are not preserved, either.
Compiling Compiling
^^^^^^^^^ ^^^^^^^^^
To compile your project, you must give DMD the directories containing import To compile your project, DMD needs to know which directories contain the
modules and the library. You also need to tell it to link with D:YAML. The import imported modules and the library. You also need to tell it to link with D:YAML.
directory should be the D:YAML package directory. You can specify it using the The import directory should be the D:YAML package directory. You can specify it
``-I`` option of DMD. The library directory should be where you put the compiled using the ``-I`` option of DMD. The library directory should point to the
D:YAML library. On Unix/Linux you can specify it using the ``-L-L`` option, and compiled library. On Unix/Linux you can specify it using the ``-L-L`` option,
link with D:YAML using the ``-L-l`` option. On Windows, the import directory is and link with D:YAML using the ``-L-l`` option. On Windows, the import directory
used as the library directory. To link with the library on Windows, just add the is used as the library directory. To link with the library on Windows, just add
path to it relative to the current directory. the path to it relative to the current directory.
For example, if you extracted and compiled D:YAML in ``/home/xxx/dyaml``, your For example, if you extracted and compiled D:YAML in ``/home/xxx/dyaml``, your
project is in ``/home/xxx/dyaml-project``, and you are currently in that project is in ``/home/xxx/dyaml-project``, and you are currently in that
directory, you can compile the project with the following command on Unix/Linux:: directory, compile the project with the following command on Unix/Linux::
dmd -I../dyaml -L-L../dyaml -L-ldyaml main.d dmd -I../dyaml -L-L../dyaml -L-ldyaml main.d

View file

@ -15,6 +15,7 @@ module dyaml.constructor;
import std.array; import std.array;
import std.algorithm; import std.algorithm;
import std.base64; import std.base64;
import std.container;
import std.conv; import std.conv;
import std.datetime; import std.datetime;
import std.exception; import std.exception;
@ -59,6 +60,7 @@ private alias ConstructorException Error;
* Constructor uses user-specifyable functions to create a node of desired * Constructor uses user-specifyable functions to create a node of desired
* data type from a scalar, sequence or mapping. * data type from a scalar, sequence or mapping.
* *
*
* Each of these functions is associated with a tag, and can process either * Each of these functions is associated with a tag, and can process either
* a scalar, a sequence, or a mapping. The constructor passes each value to * a scalar, a sequence, or a mapping. The constructor passes each value to
* the function with corresponding tag, which then returns the resulting value * the function with corresponding tag, which then returns the resulting value
@ -130,10 +132,19 @@ final class Constructor
* its message will be added to a YAMLException that will also tell the * its message will be added to a YAMLException that will also tell the
* user which type failed to construct, and position in the file. * 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.
* *
*
* Structs and classes must implement the $(D opCmp()) operator for D:YAML
* support. The signature of the operator that must be implemented
* is $(D const int opCmp(ref const MyStruct s)) for structs where
* $(I MyStruct) is the struct type, and $(D int opCmp(Object o)) for
* classes. Note that the class $(D opCmp()) should not alter the compared
* values - it is not const for compatibility reasons.
*
* Params: tag = Tag for the function to handle. * Params: tag = Tag for the function to handle.
* ctor = Constructor function. * ctor = Constructor function.
* *
@ -147,6 +158,16 @@ final class Constructor
* struct MyStruct * struct MyStruct
* { * {
* int x, y, z; * int x, y, z;
*
* //Any D:YAML type must have a custom opCmp operator.
* //This is used for ordering in mappings.
* const int opCmp(ref const MyStruct s)
* {
* if(x != s.x){return x - s.x;}
* if(y != s.y){return y - s.y;}
* if(z != s.z){return z - s.z;}
* return 0;
* }
* } * }
* *
* MyStruct constructMyStructScalar(ref Node node) * MyStruct constructMyStructScalar(ref Node node)
@ -190,6 +211,16 @@ final class Constructor
* struct MyStruct * struct MyStruct
* { * {
* int x, y, z; * int x, y, z;
*
* //Any D:YAML type must have a custom opCmp operator.
* //This is used for ordering in mappings.
* const int opCmp(ref const MyStruct s)
* {
* if(x != s.x){return x - s.x;}
* if(y != s.y){return y - s.y;}
* if(z != s.z){return z - s.z;}
* return 0;
* }
* } * }
* *
* MyStruct constructMyStructSequence(ref Node node) * MyStruct constructMyStructSequence(ref Node node)
@ -231,6 +262,16 @@ final class Constructor
* struct MyStruct * struct MyStruct
* { * {
* int x, y, z; * int x, y, z;
*
* //Any D:YAML type must have a custom opCmp operator.
* //This is used for ordering in mappings.
* const int opCmp(ref const MyStruct s)
* {
* if(x != s.x){return x - s.x;}
* if(y != s.y){return y - s.y;}
* if(z != s.z){return z - s.z;}
* return 0;
* }
* } * }
* *
* MyStruct constructMyStructMapping(ref Node node) * MyStruct constructMyStructMapping(ref Node node)
@ -330,19 +371,19 @@ final class Constructor
} }
///Construct a null node. ///Construct a _null _node.
YAMLNull constructNull(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(ref Node node) YAMLMerge constructMerge(ref Node node)
{ {
return YAMLMerge(); return YAMLMerge();
} }
///Construct a boolean node. ///Construct a boolean _node.
bool constructBool(ref Node node) bool constructBool(ref Node node)
{ {
static yes = ["yes", "true", "on"]; static yes = ["yes", "true", "on"];
@ -353,7 +394,7 @@ bool constructBool(ref Node node)
throw new Exception("Unable to parse boolean value: " ~ value); throw new Exception("Unable to parse boolean value: " ~ value);
} }
///Construct an integer (long) node. ///Construct an integer (long) _node.
long constructLong(ref Node node) long constructLong(ref Node node)
{ {
string value = node.as!string().replace("_", ""); string value = node.as!string().replace("_", "");
@ -421,7 +462,7 @@ unittest
assert(685230 == getLong(sexagesimal)); assert(685230 == getLong(sexagesimal));
} }
///Construct a floating point (real) node. ///Construct a floating point (real) _node.
real constructReal(ref Node node) real constructReal(ref Node node)
{ {
string value = node.as!string().replace("_", "").toLower(); string value = node.as!string().replace("_", "").toLower();
@ -491,7 +532,7 @@ unittest
assert(to!string(getReal(NaN)) == "nan"); assert(to!string(getReal(NaN)) == "nan");
} }
///Construct a binary (base64) node. ///Construct a binary (base64) _node.
ubyte[] constructBinary(ref Node node) ubyte[] constructBinary(ref Node node)
{ {
string value = node.as!string; string value = node.as!string;
@ -519,7 +560,7 @@ unittest
assert(value == test); assert(value == test);
} }
///Construct a timestamp (SysTime) node. ///Construct a timestamp (SysTime) _node.
SysTime constructTimestamp(ref Node node) SysTime constructTimestamp(ref Node node)
{ {
string value = node.as!string; string value = node.as!string;
@ -618,7 +659,7 @@ unittest
assert(timestamp(ymd) == "20021214T000000Z"); assert(timestamp(ymd) == "20021214T000000Z");
} }
///Construct a string node. ///Construct a string _node.
string constructString(ref Node node) string constructString(ref Node node)
{ {
return node.as!string; return node.as!string;
@ -641,22 +682,22 @@ Node.Pair[] getPairs(string type, Node[] nodes)
return pairs; return pairs;
} }
///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(ref Node node) Node.Pair[] constructOrderedMap(ref Node node)
{ {
auto pairs = getPairs("ordered map", node.as!(Node[])); auto pairs = getPairs("ordered map", node.as!(Node[]));
//TODO: the map here should be replaced with something with deterministic //Detect duplicates.
//memory allocation if possible. //TODO this should be replaced by something with deterministic memory allocation.
//Detect duplicates. auto keys = redBlackTree!Node();
bool[Node] map; scope(exit){clear(keys);}
foreach(ref pair; pairs) foreach(ref pair; pairs)
{ {
enforce((pair.key in map) is null, enforce(!(pair.key in keys),
new Exception("Duplicate entry in an ordered map")); new Exception("Duplicate entry in an ordered map: "
map[pair.key] = true; ~ pair.key.debugString()));
keys.insert(pair.key);
} }
clear(map);
return pairs; return pairs;
} }
unittest unittest
@ -701,13 +742,13 @@ unittest
assert(!hasDuplicates(alternateTypes(64))); assert(!hasDuplicates(alternateTypes(64)));
} }
///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(ref Node node) Node.Pair[] constructPairs(ref Node node)
{ {
return getPairs("pairs", node.as!(Node[])); return getPairs("pairs", node.as!(Node[]));
} }
///Construct a set node. ///Construct a set _node.
Node[] constructSet(ref Node node) Node[] constructSet(ref Node node)
{ {
auto pairs = node.as!(Node.Pair[]); auto pairs = node.as!(Node.Pair[]);
@ -771,26 +812,26 @@ unittest
(constructSet(Node(noDuplicatesLong.dup)))); (constructSet(Node(noDuplicatesLong.dup))));
} }
///Construct a sequence (array) node. ///Construct a sequence (array) _node.
Node[] constructSequence(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(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 //Detect duplicates.
//memory allocation if possible. //TODO this should be replaced by something with deterministic memory allocation.
//Detect duplicates. auto keys = redBlackTree!Node();
bool[Node] map; scope(exit){clear(keys);}
scope(exit){clear(map);}
foreach(ref pair; pairs) foreach(ref pair; pairs)
{ {
enforce((pair.key in map) is null, enforce(!(pair.key in keys),
new Exception("Duplicate entry in a map")); new Exception("Duplicate entry in a map: "
map[pair.key] = true; ~ pair.key.debugString()));
keys.insert(pair.key);
} }
return pairs; return pairs;
} }
@ -805,6 +846,14 @@ import dyaml.loader;
struct MyStruct struct MyStruct
{ {
int x, y, z; int x, y, z;
const int opCmp(ref const MyStruct s)
{
if(x != s.x){return x - s.x;}
if(y != s.y){return y - s.y;}
if(z != s.z){return z - s.z;}
return 0;
}
} }
MyStruct constructMyStructScalar(ref Node node) MyStruct constructMyStructScalar(ref Node node)

View file

@ -69,16 +69,16 @@ package struct YAMLMerge{}
package abstract class YAMLObject package abstract class YAMLObject
{ {
public: public:
///Get type of the stored value. //Get type of the stored value.
@property TypeInfo type() const {assert(false);} @property TypeInfo type() const {assert(false);}
protected: protected:
///Test for equality with another YAMLObject. //Compare with another YAMLObject.
bool equals(const YAMLObject rhs) const {assert(false);} int cmp(const YAMLObject rhs) const {assert(false);};
} }
//Stores a user defined YAML data type. //Stores a user defined YAML data type.
package class YAMLContainer(T) if (!Node.Value.allowed!T): YAMLObject package class YAMLContainer(T) if (!Node.allowed!T): YAMLObject
{ {
private: private:
//Stored value. //Stored value.
@ -102,11 +102,16 @@ package class YAMLContainer(T) if (!Node.Value.allowed!T): YAMLObject
} }
protected: protected:
//Test for equality with another YAMLObject. //Compare with another YAMLObject.
override bool equals(const YAMLObject rhs) const override int cmp(const YAMLObject rhs) const
{ {
if(rhs.type !is typeid(T)){return false;} const typeCmp = type.opCmp(rhs.type);
return cast(T)value_ == (cast(const YAMLContainer)rhs).value_; if(typeCmp != 0){return typeCmp;}
//Const-casting here as Object opCmp is not const.
T* v1 = cast(T*)&value_;
T* v2 = cast(T*)&((cast(YAMLContainer)rhs).value_);
return (*v1).opCmp(*v2);
} }
private: private:
@ -149,20 +154,21 @@ struct Node
///Equality test with another Pair. ///Equality test with another Pair.
bool opEquals(const ref Pair rhs) const bool opEquals(const ref Pair rhs) const
{ {
return equals!true(rhs); return cmp!true(rhs) == 0;
} }
private: private:
/* /*
* Equality test with another Pair. * Comparison with another Pair.
* *
* useTag determines whether or not we consider node tags * useTag determines whether or not we consider node tags
* in the test. * in the comparison.
*/ */
bool equals(bool useTag)(ref const(Pair) rhs) const int cmp(bool useTag)(ref const(Pair) rhs) const
{ {
return key.equals!(useTag)(rhs.key) && const keyCmp = key.cmp!useTag(rhs.key);
value.equals!(useTag)(rhs.value); return keyCmp != 0 ? keyCmp
: value.cmp!useTag(rhs.value);
} }
} }
@ -171,6 +177,15 @@ struct Node
alias Algebraic!(YAMLNull, YAMLMerge, bool, long, real, ubyte[], SysTime, string, alias Algebraic!(YAMLNull, YAMLMerge, bool, long, real, ubyte[], SysTime, string,
Node.Pair[], Node[], YAMLObject) Value; Node.Pair[], Node[], YAMLObject) Value;
//Can Value hold this type without wrapping it in a YAMLObject?
template allowed(T)
{
enum allowed = isIntegral!T ||
isFloatingPoint!T ||
isSomeString!T ||
Value.allowed!T;
}
private: private:
///Stored value. ///Stored value.
Value value_; Value value_;
@ -186,8 +201,6 @@ struct Node
CollectionStyle collectionStyle = CollectionStyle.Invalid; CollectionStyle collectionStyle = CollectionStyle.Invalid;
public: public:
@disable int opCmp(ref Node);
/** /**
* Construct a Node from a value. * Construct a Node from a value.
* *
@ -518,7 +531,7 @@ struct Node
if(isType!T){return value_.get!T;} if(isType!T){return value_.get!T;}
///Must go before others, as even string/int/etc could be stored in a YAMLObject. ///Must go before others, as even string/int/etc could be stored in a YAMLObject.
static if(!Value.allowed!T) if(isUserType) static if(!allowed!T) if(isUserType)
{ {
auto object = as!YAMLObject; auto object = as!YAMLObject;
if(object.type is typeid(T)) if(object.type is typeid(T))
@ -569,15 +582,15 @@ struct Node
//Const version of get. //Const version of get.
@property T get(T)() const if(is(T == const)) @property T get(T)() const if(is(T == const))
{ {
if(isType!T){return value_.get!T;} if(isType!(Unqual!T)){return value_.get!T;}
///Must go before others, as even string/int/etc could be stored in a YAMLObject. ///Must go before others, as even string/int/etc could be stored in a YAMLObject.
static if(!Value.allowed!T) if(isUserType) static if(!allowed!(Unqual!T)) if(isUserType)
{ {
auto object = as!(const YAMLObject); auto object = as!(const YAMLObject);
if(object.type is typeid(T)) if(object.type is typeid(T))
{ {
return (cast(const YAMLContainer!T)object).value_; return (cast(const YAMLContainer!(Unqual!T))object).value_;
} }
throw new Error("Node has unexpected type: " ~ object.type.toString ~ throw new Error("Node has unexpected type: " ~ object.type.toString ~
". Expected: " ~ typeid(T).toString, startMark_); ". Expected: " ~ typeid(T).toString, startMark_);
@ -781,7 +794,7 @@ struct Node
} }
/** /**
* Iterate over a sequence, getting each element as T. * Foreach over a sequence, getting each element as T.
* *
* If T is Node, simply iterate over the nodes in the sequence. * If T is Node, simply iterate over the nodes in the sequence.
* Otherwise, convert each node to T during iteration. * Otherwise, convert each node to T during iteration.
@ -838,7 +851,7 @@ struct Node
} }
/** /**
* Iterate over a mapping, getting each key/value as K/V. * Foreach over a mapping, getting each key/value as K/V.
* *
* If the K and/or V is Node, simply iterate over the nodes in the mapping. * If the K and/or V is Node, simply iterate over the nodes in the mapping.
* Otherwise, convert each key/value to T during iteration. * Otherwise, convert each key/value to T during iteration.
@ -1127,6 +1140,20 @@ struct Node
} }
} }
///Compare with another _node.
const int opCmp(ref const Node node)
{
return cmp!true(node);
}
//Compute hash of the node.
const hash_t toHash()
{
const tagHash = tag_.isNull ? 0 : tag_.toHash();
//Variant toHash is not const at the moment, so we need to const-cast.
return tagHash + (cast(Value)value_).toHash();
}
package: package:
/* /*
* Construct a node from raw data. * Construct a node from raw data.
@ -1168,68 +1195,138 @@ struct Node
{ {
static if(is(Unqual!T == Node)) static if(is(Unqual!T == Node))
{ {
static if(useTag) return cmp!useTag(rhs) == 0;
{
if(tag_ != rhs.tag_){return false;}
}
if(!isValid){return !rhs.isValid;}
if(!rhs.isValid || !hasEqualType(rhs))
{
return false;
}
static bool compareCollection(T)(const ref Node lhs, const ref Node rhs)
{
const c1 = lhs.value_.get!(const T);
const c2 = rhs.value_.get!(const T);
if(c1 is c2){return true;}
if(c1.length != c2.length){return false;}
foreach(i; 0 .. c1.length)
{
if(!c1[i].equals!useTag(c2[i])){return false;}
}
return true;
}
static bool compare(T)(const ref Node lhs, const ref Node rhs)
{
return lhs.value_.get!(const T) == rhs.value_.get!(const T);
}
if(isSequence) {return compareCollection!(Node[])(this, rhs);}
else if(isMapping){return compareCollection!(Pair[])(this, rhs);}
else if(isString) {return compare!string(this, rhs);}
else if(isInt) {return compare!long(this, rhs);}
else if(isBool) {return compare!bool(this, rhs);}
else if(isBinary) {return compare!(ubyte[])(this, rhs);}
else if(isNull) {return true;}
else if(isFloat)
{
const r1 = value_.get!(const real);
const r2 = rhs.value_.get!(const real);
return isNaN(r1) ? isNaN(r2)
: (r1 <= r2 + real.epsilon && r1 >= r2 - real.epsilon);
}
else if(isTime)
{
const t1 = value_.get!(const SysTime);
const t2 = rhs.value_.get!(const SysTime);
return t1 == t2;
}
else if(isUserType)
{
return value_.get!(const YAMLObject).equals(rhs.value_.get!(const YAMLObject));
}
assert(false, "Unknown kind of node (equality comparison) : " ~ type.toString);
} }
else else
{ {
try{return rhs == get!T;} try
{
static if(is(T == const))
{
return rhs == get!T;
}
else
{
return rhs == get!(const(Unqual!T));
}
}
catch(NodeException e){return false;} catch(NodeException e){return false;}
} }
} }
/*
* Comparison with another node.
*
* Used for ordering in mappings and for opEquals.
*
* useTag determines whether or not to consider tags in the comparison.
*/
int cmp(bool useTag)(const ref Node rhs) const
{
//Compare tags - if equal or both null, we need to compare further.
static if(useTag)
{
const tagCmp = tag_.isNull ? rhs.tag_.isNull ? 0 : -1
: rhs.tag_.isNull ? 1 : tag_.opCmp(rhs.tag_);
if(tagCmp != 0){return tagCmp;}
}
static int cmp(T1, T2)(T1 a, T2 b)
{
return a > b ? 1 :
a < b ? -1 :
0;
}
//Compare validity: if both valid, we have to compare further.
const v1 = isValid;
const v2 = rhs.isValid;
if(!v1){return v2 ? -1 : 0;}
if(!v2){return 1;}
const typeCmp = type.opCmp(rhs.type);
if(typeCmp != 0){return typeCmp;}
static int compareCollections(T)(const ref Node lhs, const ref Node rhs)
{
const c1 = lhs.value_.get!(const T);
const c2 = rhs.value_.get!(const T);
if(c1 is c2){return 0;}
if(c1.length != c2.length)
{
return cmp(c1.length, c2.length);
}
//Equal lengths, compare items.
foreach(i; 0 .. c1.length)
{
const itemCmp = c1[i].cmp!useTag(c2[i]);
if(itemCmp != 0){return itemCmp;}
}
return 0;
}
if(isSequence){return compareCollections!(Node[])(this, rhs);}
if(isMapping) {return compareCollections!(Pair[])(this, rhs);}
if(isString)
{
return std.algorithm.cmp(value_.get!(const string),
rhs.value_.get!(const string));
}
if(isInt)
{
return cmp(value_.get!(const long), rhs.value_.get!(const long));
}
if(isBool)
{
const b1 = value_.get!(const bool);
const b2 = rhs.value_.get!(const bool);
return b1 ? b2 ? 0 : 1
: b2 ? -1 : 0;
}
if(isBinary)
{
const b1 = value_.get!(const ubyte[]);
const b2 = rhs.value_.get!(const ubyte[]);
return std.algorithm.cmp(b1, b2);
}
if(isNull)
{
return 0;
}
//Floats need special handling for NaNs .
//We consider NaN to be lower than any float.
if(isFloat)
{
const r1 = value_.get!(const real);
const r2 = rhs.value_.get!(const real);
if(isNaN(r1))
{
return isNaN(r2) ? 0 : -1;
}
if(isNaN(r2))
{
return 1;
}
//Fuzzy equality.
if(r1 <= r2 + real.epsilon && r1 >= r2 - real.epsilon)
{
return 0;
}
return cmp(r1, r2);
}
else if(isTime)
{
const t1 = value_.get!(const SysTime);
const t2 = rhs.value_.get!(const SysTime);
return cmp(t1, t2);
}
else if(isUserType)
{
return value_.get!(const YAMLObject).cmp(rhs.value_.get!(const YAMLObject));
}
assert(false, "Unknown type of node for comparison : " ~ type.toString);
}
/* /*
* Get a string representation of the node tree. Used for debugging. * Get a string representation of the node tree. Used for debugging.
* *

View file

@ -16,6 +16,7 @@ module dyaml.representer;
import std.algorithm; import std.algorithm;
import std.array; import std.array;
import std.base64; import std.base64;
import std.container;
import std.conv; import std.conv;
import std.datetime; import std.datetime;
import std.exception; import std.exception;
@ -37,7 +38,7 @@ class RepresenterException : YAMLException
} }
/** /**
* Represents YAML nodes of various data types as scalar, sequence and mapping nodes ready for output. * Represents YAML nodes as scalar, sequence and mapping nodes ready for output.
* *
* This class is used to add support for dumping of custom data types. * This class is used to add support for dumping of custom data types.
* *
@ -86,13 +87,13 @@ final class Representer
representers_ = null; representers_ = null;
} }
///Set default _style for scalars. Invalid means the _style is chosen automatically. ///Set default _style for scalars. If style is $(D ScalarStyle.Invalid), the _style is chosen automatically.
@property void defaultScalarStyle(ScalarStyle style) @property void defaultScalarStyle(ScalarStyle style)
{ {
defaultScalarStyle_ = style; defaultScalarStyle_ = style;
} }
///Set default _style for collections. Invalid means the _style is chosen automatically. ///Set default _style for collections. If style is $(D CollectionStyle.Invalid), the _style is chosen automatically.
@property void defaultCollectionStyle(CollectionStyle style) @property void defaultCollectionStyle(CollectionStyle style)
{ {
defaultCollectionStyle_ = style; defaultCollectionStyle_ = style;
@ -101,13 +102,22 @@ 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 references to a Node storing the data * The representer function takes references to a $(D Node) storing the data
* type and to the Representer. It returns the represented node and may * type and to the $(D Representer). It returns the represented node and may
* throw a RepresenterException. See the example for more information. * throw a $(D 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 the * types already have representer functions unless disabled in the
* Representer constructor. * $(D Representer) constructor.
*
*
* Structs and classes must implement the $(D opCmp()) operator for D:YAML
* support. The signature of the operator that must be implemented
* is $(D const int opCmp(ref const MyStruct s)) for structs where
* $(I MyStruct) is the struct type, and $(D int opCmp(Object o)) for
* classes. Note that the class $(D opCmp()) should not alter the compared
* values - it is not const for compatibility reasons.
* *
* Params: representer = Representer function to add. * Params: representer = Representer function to add.
* *
@ -122,6 +132,16 @@ final class Representer
* struct MyStruct * struct MyStruct
* { * {
* int x, y, z; * int x, y, z;
*
* //Any D:YAML type must have a custom opCmp operator.
* //This is used for ordering in mappings.
* const int opCmp(ref const MyStruct s)
* {
* if(x != s.x){return x - s.x;}
* if(y != s.y){return y - s.y;}
* if(z != s.z){return z - s.z;}
* return 0;
* }
* } * }
* *
* Node representMyStruct(ref Node node, Representer representer) * Node representMyStruct(ref Node node, Representer representer)
@ -161,12 +181,16 @@ final class Representer
* this.z = z; * this.z = z;
* } * }
* *
* ///We need custom opEquals for node equality, as default opEquals compares references. * //Any D:YAML type must have a custom opCmp operator.
* override bool opEquals(Object rhs) * //This is used for ordering in mappings.
* override int opCmp(Object o)
* { * {
* if(typeid(rhs) != typeid(MyClass)){return false;} * MyClass s = cast(MyClass)o;
* auto t = cast(MyClass)rhs; * if(s is null){return -1;}
* return x == t.x && y == t.y && z == t.z; * if(x != s.x){return x - s.x;}
* if(y != s.y){return y - s.y;}
* if(z != s.z){return z - s.z;}
* return 0;
* } * }
* *
* ///Useful for Node.as!string . * ///Useful for Node.as!string .
@ -226,6 +250,16 @@ final class Representer
* struct MyStruct * struct MyStruct
* { * {
* int x, y, z; * int x, y, z;
*
* //Any D:YAML type must have a custom opCmp operator.
* //This is used for ordering in mappings.
* const int opCmp(ref const MyStruct s)
* {
* if(x != s.x){return x - s.x;}
* if(y != s.y){return y - s.y;}
* if(z != s.z){return z - s.z;}
* return 0;
* }
* } * }
* *
* Node representMyStruct(ref Node node, Representer representer) * Node representMyStruct(ref Node node, Representer representer)
@ -233,7 +267,6 @@ final class Representer
* auto value = node.as!MyStruct; * auto value = node.as!MyStruct;
* auto scalar = format(value.x, ":", value.y, ":", value.z); * auto scalar = format(value.x, ":", value.y, ":", value.z);
* return representer.representScalar("!mystruct.tag", scalar); * return representer.representScalar("!mystruct.tag", scalar);
*
* } * }
* -------------------- * --------------------
*/ */
@ -250,20 +283,30 @@ final class Representer
* *
* This is used by representer functions that produce sequences. * This is used by representer functions that produce sequences.
* *
* Params: tag = Tag of the sequence. * Params: tag = Tag of the _sequence.
* sequence = Sequence of nodes. * sequence = Sequence of nodes.
* style = Style of the _sequence. If invalid, default _style will be used. * style = Style of the _sequence. If invalid, default _style will be used.
* If the node was loaded before, previous _style will always be used. * If the node was loaded before, previous _style will always be used.
* *
* Returns: The represented node. * Returns: The represented node.
* *
* Throws: RepresenterException if a child could not be represented. * Throws: $(D RepresenterException) if a child could not be represented.
* *
* Example: * Example:
* -------------------- * --------------------
* struct MyStruct * struct MyStruct
* { * {
* int x, y, z; * int x, y, z;
*
* //Any D:YAML type must have a custom opCmp operator.
* //This is used for ordering in mappings.
* const int opCmp(ref const MyStruct s)
* {
* if(x != s.x){return x - s.x;}
* if(y != s.y){return y - s.y;}
* if(z != s.z){return z - s.z;}
* return 0;
* }
* } * }
* *
* Node representMyStruct(ref Node node, Representer representer) * Node representMyStruct(ref Node node, Representer representer)
@ -311,18 +354,28 @@ final class Representer
* *
* Params: tag = Tag of the mapping. * Params: tag = Tag of the mapping.
* pairs = Key-value _pairs of the mapping. * pairs = Key-value _pairs of the mapping.
* style = Style of the _mapping. If invalid, default _style will be used. * style = Style of the mapping. If invalid, default _style will be used.
* If the node was loaded before, previous _style will always be used. * If the node was loaded before, previous _style will always be used.
* *
* Returns: The represented node. * Returns: The represented node.
* *
* Throws: RepresenterException if a child could not be represented. * Throws: $(D RepresenterException) if a child could not be represented.
* *
* Example: * Example:
* -------------------- * --------------------
* struct MyStruct * struct MyStruct
* { * {
* int x, y, z; * int x, y, z;
*
* //Any D:YAML type must have a custom opCmp operator.
* //This is used for ordering in mappings.
* const int opCmp(ref const MyStruct s)
* {
* if(x != s.x){return x - s.x;}
* if(y != s.y){return y - s.y;}
* if(z != s.z){return z - s.z;}
* return 0;
* }
* } * }
* *
* Node representMyStruct(ref Node node, Representer representer) * Node representMyStruct(ref Node node, Representer representer)
@ -496,14 +549,13 @@ Node representPairs(ref Node node, Representer representer)
bool hasDuplicates(Node.Pair[] pairs) bool hasDuplicates(Node.Pair[] pairs)
{ {
//TODO The map here should be replaced with something with deterministic. //TODO this should be replaced by something with deterministic memory allocation.
//memory allocation if possible. auto keys = redBlackTree!Node();
bool[Node] map; scope(exit){clear(keys);}
scope(exit){clear(map);}
foreach(ref pair; pairs) foreach(ref pair; pairs)
{ {
if((pair.key in map) !is null){return true;} if(pair.key in keys){return true;}
map[pair.key] = true; keys.insert(pair.key);
} }
return false; return false;
} }
@ -547,6 +599,14 @@ import dyaml.dumper;
struct MyStruct struct MyStruct
{ {
int x, y, z; int x, y, z;
const int opCmp(ref const MyStruct s)
{
if(x != s.x){return x - s.x;}
if(y != s.y){return y - s.y;}
if(z != s.z){return z - s.z;}
return 0;
}
} }
Node representMyStruct(ref Node node, Representer representer) Node representMyStruct(ref Node node, Representer representer)
@ -585,13 +645,15 @@ class MyClass
this.y = y; this.y = y;
this.z = z; this.z = z;
} }
///We need custom opEquals for node equality, as default opEquals compares references. override int opCmp(Object o)
override bool opEquals(Object rhs)
{ {
if(typeid(rhs) != typeid(MyClass)){return false;} MyClass s = cast(MyClass)o;
auto t = cast(MyClass)rhs; if(s is null){return -1;}
return x == t.x && y == t.y && z == t.z; if(x != s.x){return x - s.x;}
if(y != s.y){return y - s.y;}
if(z != s.z){return z - s.z;}
return 0;
} }
///Useful for Node.as!string . ///Useful for Node.as!string .

View file

@ -8,6 +8,14 @@ struct Color
ubyte red; ubyte red;
ubyte green; ubyte green;
ubyte blue; ubyte blue;
const int opCmp(ref const Color c)
{
if(red != c.red) {return red - c.red;}
if(green != c.green){return green - c.green;}
if(blue != c.blue) {return blue - c.blue;}
return 0;
}
} }
Color constructColorScalar(ref Node node) Color constructColorScalar(ref Node node)

View file

@ -6,6 +6,14 @@ struct Color
ubyte red; ubyte red;
ubyte green; ubyte green;
ubyte blue; ubyte blue;
const int opCmp(ref const Color c)
{
if(red != c.red) {return red - c.red;}
if(green != c.green){return green - c.green;}
if(blue != c.blue) {return blue - c.blue;}
return 0;
}
} }
Node representColor(ref Node node, Representer representer) Node representColor(ref Node node, Representer representer)

View file

@ -8,6 +8,14 @@ struct Color
ubyte red; ubyte red;
ubyte green; ubyte green;
ubyte blue; ubyte blue;
const int opCmp(ref const Color c)
{
if(red != c.red) {return red - c.red;}
if(green != c.green){return green - c.green;}
if(blue != c.blue) {return blue - c.blue;}
return 0;
}
} }
Color constructColorScalar(ref Node node) Color constructColorScalar(ref Node node)

View file

@ -312,11 +312,16 @@ class TestClass
this.z = z; this.z = z;
} }
override bool opEquals(Object rhs) //Any D:YAML type must have a custom opCmp operator.
//This is used for ordering in mappings.
override int opCmp(Object o)
{ {
if(typeid(rhs) != typeid(TestClass)){return false;} TestClass s = cast(TestClass)o;
auto t = cast(TestClass)rhs; if(s is null){return -1;}
return x == t.x && y == t.y && z == t.z; if(x != s.x){return x - s.x;}
if(y != s.y){return y - s.y;}
if(z != s.z){return z - s.z;}
return 0;
} }
override string toString() override string toString()
@ -330,10 +335,12 @@ struct TestStruct
{ {
int value; int value;
bool opEquals(const ref TestStruct rhs) const //Any D:YAML type must have a custom opCmp operator.
//This is used for ordering in mappings.
const int opCmp(ref const TestStruct s)
{ {
return value == rhs.value; return value - s.value;
} }
} }
///Constructor function for TestClass. ///Constructor function for TestClass.