Changed the Constructor API (for loading of custom types) to

make it easier to load custom classes/structs. Updated API docs,
tutorials and examples accordingly.
This commit is contained in:
Ferdinand Majerech 2011-10-17 12:53:38 +02:00
parent 5547f62176
commit 548480b06b
19 changed files with 371 additions and 396 deletions

Binary file not shown.

View file

@ -20,8 +20,9 @@ Constructor
D:YAML uses the `Constructor <../api/dyaml.constructor.html>`_ class to process
each node to hold data type corresponding to its tag. *Constructor* stores a
function for each supported tag to process it. These functions are supplied by
the user using the *addConstructor()* method. *Constructor* is then passed to
*Loader*, which parses YAML input.
the user using the *addConstructorXXX()* methods, where *XXX* is *Scalar*,
*Sequence* or *Mapping*. *Constructor* is then passed to *Loader*, which parses
YAML input.
Struct types have no specific requirements for YAML support. Class types should
define the *opEquals()* operator, as this is used in equality comparisons of
@ -41,17 +42,20 @@ following struct:
}
First, we need a function to construct our data type. It must take two *Mark*
structs, which store position of the node in the file, and either a *string*, an
array of *Node* or of *Node.Pair*, depending on whether we're constructing our
value from a scalar, sequence, or mapping, respectively. In this tutorial, we
have functions to construct a color from a scalar, using HTML-like format,
RRGGBB, or from a mapping, where we use the following format:
{r:RRR, g:GGG, b:BBB} . Code of these functions:
structs, which store position of the node in the file, and a reference to *Node*
to construct from. The node is guaranteed to contain either a *string*, an array
of *Node* or of *Node.Pair*, depending on whether we're constructing our value
from a scalar, sequence, or mapping, respectively. 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
Color constructColorScalar(Mark start, Mark end, string value)
Color constructColorScalar(Mark start, Mark end, ref Node node)
{
string value = node.get!string;
if(value.length != 6)
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
@ -82,30 +86,21 @@ RRGGBB, or from a mapping, where we use the following format:
return result;
}
Color constructColorMapping(Mark start, Mark end, Node.Pair[] pairs)
Color constructColorMapping(Mark start, Mark end, ref Node node)
{
int r, g, b;
r = g = b = -1;
bool error = pairs.length != 3;
int r,g,b;
bool error = false;
foreach(ref pair; pairs)
//Might throw if a value is missing or is not an integer.
try
{
//Key might not be a string, and value might not be an int,
//so we need to check for that
try
{
switch(pair.key.get!string)
{
case "r": r = pair.value.get!int; break;
case "g": g = pair.value.get!int; break;
case "b": b = pair.value.get!int; break;
default: error = true;
}
}
catch(NodeException e)
{
error = true;
}
r = node["r"].get!int;
g = node["g"].get!int;
b = node["b"].get!int;
}
catch(NodeException e)
{
error = true;
}
if(error || r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255)
@ -146,8 +141,8 @@ Finally, the code to put it all together:
{
auto constructor = new Constructor;
//both functions handle the same tag, but one handles scalar, one mapping.
constructor.addConstructor("!color", &constructColorScalar);
constructor.addConstructor("!color-mapping", &constructColorMapping);
constructor.addConstructorScalar("!color", &constructColorScalar);
constructor.addConstructorMapping("!color-mapping", &constructColorMapping);
auto loader = Loader("input.yaml");
loader.constructor = constructor;
@ -284,7 +279,7 @@ With the following code, we will add support for dumping the our Color type.
}
First we get the *Color* from the node. Then we convert it to a string with the
HTML-like format we've used before. Finally, we use the *representScalar()*
CSS-like format we've used before. Finally, we use the *representScalar()*
method of *Representer* to get a scalar node ready for output.
There are corresponding *representMapping()* and *representSequence()* methods
as well, with examples in the

View file

@ -94,14 +94,15 @@
</table></div>
</dd>
<dt class="d_decl">void <a name="addConstructor"></a><span class="ddoc_psymbol">addConstructor</span>(T, U)(in string <b>tag</b>, T function(Mark, Mark, U) <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(Mark, Mark, ref Node) <b>ctor</b>);
</dt>
<dd><p>Add a constructor function.
<dd><p>Add a constructor function from scalar.
</p>
<p>The function passed must two Marks (determining start and end positions of
the node in file) and either a string (if constructing from scalar),
an array of Nodes (from sequence) or an array of Node.Pair (from mapping).
The value returned by this function will be stored in the resulring node.
<p>The function must take two Marks (start and end positions of
the node in file) and a reference to Node to construct from.
The node contains a string for scalars, Node[] for sequences and
Node.Pair[] for mappings.
The value returned by this function will be stored in the resulting node.
<br>
Only one constructor function can be set for one tag.
@ -113,47 +114,61 @@
<td valign=top>Constructor function.</td></tr>
</table></div>
</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>
<dd><p>Add a constructor function from sequence.
</p>
<b>See Also:</b><div class="pbr">addConstructorScalar</div>
</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>
<dd><p>Add a constructor function from a mapping.
</p>
<b>See Also:</b><div class="pbr">addConstructorScalar</div>
</dd>
</dl>
</dd>
<dt class="d_decl">YAMLNull <a name="constructNull"></a><span class="ddoc_psymbol">constructNull</span>(Mark <b>start</b>, Mark <b>end</b>, string <b>value</b>);
<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>
<dd><p>Construct a <b>null</b> node.</p>
<dd><p>Construct a <b>null</b> <b>node</b>.</p>
</dd>
<dt class="d_decl">YAMLMerge <a name="constructMerge"></a><span class="ddoc_psymbol">constructMerge</span>(Mark <b>start</b>, Mark <b>end</b>, string <b>value</b>);
<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>
<dd><p>Construct a merge node - a node that merges another node 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>
<dt class="d_decl">bool <a name="constructBool"></a><span class="ddoc_psymbol">constructBool</span>(Mark <b>start</b>, Mark <b>end</b>, string <b>value</b>);
<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>
<dd><p>Construct a boolean node.</p>
<dd><p>Construct a boolean <b>node</b>.</p>
</dd>
<dt class="d_decl">long <a name="constructLong"></a><span class="ddoc_psymbol">constructLong</span>(Mark <b>start</b>, Mark <b>end</b>, string <b>value</b>);
<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>
<dd><p>Construct an integer (long) node.</p>
<dd><p>Construct an integer (long) <b>node</b>.</p>
</dd>
<dt class="d_decl">real <a name="constructReal"></a><span class="ddoc_psymbol">constructReal</span>(Mark <b>start</b>, Mark <b>end</b>, string <b>value</b>);
<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>
<dd><p>Construct a floating point (real) node.</p>
<dd><p>Construct a floating point (real) <b>node</b>.</p>
</dd>
<dt class="d_decl">ubyte[] <a name="constructBinary"></a><span class="ddoc_psymbol">constructBinary</span>(Mark <b>start</b>, Mark <b>end</b>, string <b>value</b>);
<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>
<dd><p>Construct a binary (base64) node.</p>
<dd><p>Construct a binary (base64) <b>node</b>.</p>
</dd>
<dt class="d_decl">SysTime <a name="constructTimestamp"></a><span class="ddoc_psymbol">constructTimestamp</span>(Mark <b>start</b>, Mark <b>end</b>, string <b>value</b>);
<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>
<dd><p>Construct a timestamp (SysTime) node.</p>
<dd><p>Construct a timestamp (SysTime) <b>node</b>.</p>
</dd>
<dt class="d_decl">string <a name="constructString"></a><span class="ddoc_psymbol">constructString</span>(Mark <b>start</b>, Mark <b>end</b>, string <b>value</b>);
<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>
<dd><p>Construct a string node.</p>
<dd><p>Construct a string <b>node</b>.</p>
</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>);
@ -161,29 +176,29 @@
<dd><p>Convert a sequence of single-element mappings into a sequence of pairs.</p>
</dd>
<dt class="d_decl">Pair[] <a name="constructOrderedMap"></a><span class="ddoc_psymbol">constructOrderedMap</span>(Mark <b>start</b>, Mark <b>end</b>, Node[] <b>nodes</b>);
<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>
<dd><p>Construct an ordered map (ordered sequence of key:value pairs without duplicates) node.</p>
<dd><p>Construct an ordered map (ordered sequence of key:value pairs without duplicates) <b>node</b>.</p>
</dd>
<dt class="d_decl">Pair[] <a name="constructPairs"></a><span class="ddoc_psymbol">constructPairs</span>(Mark <b>start</b>, Mark <b>end</b>, Node[] <b>nodes</b>);
<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>
<dd><p>Construct a pairs (ordered sequence of key: value pairs allowing duplicates) node.</p>
<dd><p>Construct a pairs (ordered sequence of key: value pairs allowing duplicates) <b>node</b>.</p>
</dd>
<dt class="d_decl">Node[] <a name="constructSet"></a><span class="ddoc_psymbol">constructSet</span>(Mark <b>start</b>, Mark <b>end</b>, Pair[] <b>pairs</b>);
<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>
<dd><p>Construct a set node.</p>
<dd><p>Construct a set <b>node</b>.</p>
</dd>
<dt class="d_decl">Node[] <a name="constructSequence"></a><span class="ddoc_psymbol">constructSequence</span>(Mark <b>start</b>, Mark <b>end</b>, Node[] <b>nodes</b>);
<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>
<dd><p>Construct a sequence (array) node.</p>
<dd><p>Construct a sequence (array) <b>node</b>.</p>
</dd>
<dt class="d_decl">Pair[] <a name="constructMap"></a><span class="ddoc_psymbol">constructMap</span>(Mark <b>start</b>, Mark <b>end</b>, Pair[] <b>pairs</b>);
<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>
<dd><p>Construct an unordered map (unordered set of key: value pairs without duplicates) node.</p>
<dd><p>Construct an unordered map (unordered set of key: value pairs without duplicates) <b>node</b>.</p>
</dd>
</dl>

View file

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

View file

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

View file

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

File diff suppressed because one or more lines are too long

View file

@ -60,8 +60,9 @@ It is also possible to implicitly resolve custom tags, as we will show later.</p
<p>D:YAML uses the <a class="reference external" href="../api/dyaml.constructor.html">Constructor</a> class to process
each node to hold data type corresponding to its tag. <em>Constructor</em> stores a
function for each supported tag to process it. These functions are supplied by
the user using the <em>addConstructor()</em> method. <em>Constructor</em> is then passed to
<em>Loader</em>, which parses YAML input.</p>
the user using 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>
<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
nodes. Default class <em>opEquals()</em> compares references, which means two identical
@ -77,14 +78,17 @@ following struct:</p>
</pre></div>
</div>
<p>First, we need a function to construct our data type. It must take two <em>Mark</em>
structs, which store position of the node in the file, and either a <em>string</em>, an
array of <em>Node</em> or of <em>Node.Pair</em>, depending on whether we&#8217;re constructing our
value from a scalar, sequence, or mapping, respectively. In this tutorial, we
have functions to construct a color from a scalar, using HTML-like format,
RRGGBB, or from a mapping, where we use the following format:
{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="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="nb">string</span> <span class="n">value</span><span class="p">)</span>
structs, which store position of the node in the file, and a reference to <em>Node</em>
to construct from. The node is guaranteed to contain either a <em>string</em>, an array
of <em>Node</em> or of <em>Node.Pair</em>, depending on whether we&#8217;re constructing our value
from a scalar, sequence, or mapping, respectively. 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:</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="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="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">get</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="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>
@ -115,30 +119,21 @@ RRGGBB, or from a mapping, where we use the following format:
<span class="k">return</span> <span class="n">result</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="n">Node</span><span class="p">.</span><span class="n">Pair</span><span class="p">[]</span> <span class="n">pairs</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="p">{</span>
<span class="kt">int</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="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="p">-</span><span class="mi">1</span><span class="p">;</span>
<span class="kt">bool</span> <span class="n">error</span> <span class="p">=</span> <span class="n">pairs</span><span class="p">.</span><span class="n">length</span> <span class="p">!=</span> <span class="mi">3</span><span class="p">;</span>
<span class="kt">int</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">bool</span> <span class="n">error</span> <span class="p">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="k">foreach</span><span class="p">(</span><span class="k">ref</span> <span class="n">pair</span><span class="p">;</span> <span class="n">pairs</span><span class="p">)</span>
<span class="c1">//Might throw if a value is missing or is not an integer.</span>
<span class="k">try</span>
<span class="p">{</span>
<span class="c1">//Key might not be a string, and value might not be an int,</span>
<span class="c1">//so we need to check for that</span>
<span class="k">try</span>
<span class="p">{</span>
<span class="k">switch</span><span class="p">(</span><span class="n">pair</span><span class="p">.</span><span class="n">key</span><span class="p">.</span><span class="n">get</span><span class="p">!</span><span class="nb">string</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">case</span> <span class="s">&quot;r&quot;</span><span class="p">:</span> <span class="n">r</span> <span class="p">=</span> <span class="n">pair</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">get</span><span class="p">!</span><span class="kt">int</span><span class="p">;</span> <span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="s">&quot;g&quot;</span><span class="p">:</span> <span class="n">g</span> <span class="p">=</span> <span class="n">pair</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">get</span><span class="p">!</span><span class="kt">int</span><span class="p">;</span> <span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="s">&quot;b&quot;</span><span class="p">:</span> <span class="n">b</span> <span class="p">=</span> <span class="n">pair</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">get</span><span class="p">!</span><span class="kt">int</span><span class="p">;</span> <span class="k">break</span><span class="p">;</span>
<span class="k">default</span><span class="p">:</span> <span class="n">error</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</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="n">error</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</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">get</span><span class="p">!</span><span class="kt">int</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">get</span><span class="p">!</span><span class="kt">int</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">get</span><span class="p">!</span><span class="kt">int</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="n">error</span> <span class="p">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span><span class="p">(</span><span class="n">error</span> <span class="p">||</span> <span class="n">r</span> <span class="p">&lt;</span> <span class="mi">0</span> <span class="p">||</span> <span class="n">r</span> <span class="p">&gt;</span> <span class="mi">255</span> <span class="p">||</span> <span class="n">g</span> <span class="p">&lt;</span> <span class="mi">0</span> <span class="p">||</span> <span class="n">g</span> <span class="p">&gt;</span> <span class="mi">255</span> <span class="p">||</span> <span class="n">b</span> <span class="p">&lt;</span> <span class="mi">0</span> <span class="p">||</span> <span class="n">b</span> <span class="p">&gt;</span> <span class="mi">255</span><span class="p">)</span>
@ -174,8 +169,8 @@ RRGGBB, or from a mapping, where we use the following format:
<span class="p">{</span>
<span class="k">auto</span> <span class="n">constructor</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Constructor</span><span class="p">;</span>
<span class="c1">//both functions handle the same tag, but one handles scalar, one mapping.</span>
<span class="n">constructor</span><span class="p">.</span><span class="n">addConstructor</span><span class="p">(</span><span class="s">&quot;!color&quot;</span><span class="p">,</span> <span class="p">&amp;</span><span class="n">constructColorScalar</span><span class="p">);</span>
<span class="n">constructor</span><span class="p">.</span><span class="n">addConstructor</span><span class="p">(</span><span class="s">&quot;!color-mapping&quot;</span><span class="p">,</span> <span class="p">&amp;</span><span class="n">constructColorMapping</span><span class="p">);</span>
<span class="n">constructor</span><span class="p">.</span><span class="n">addConstructorScalar</span><span class="p">(</span><span class="s">&quot;!color&quot;</span><span class="p">,</span> <span class="p">&amp;</span><span class="n">constructColorScalar</span><span class="p">);</span>
<span class="n">constructor</span><span class="p">.</span><span class="n">addConstructorMapping</span><span class="p">(</span><span class="s">&quot;!color-mapping&quot;</span><span class="p">,</span> <span class="p">&amp;</span><span class="n">constructColorMapping</span><span class="p">);</span>
<span class="k">auto</span> <span class="n">loader</span> <span class="p">=</span> <span class="n">Loader</span><span class="p">(</span><span class="s">&quot;input.yaml&quot;</span><span class="p">);</span>
<span class="n">loader</span><span class="p">.</span><span class="n">constructor</span> <span class="p">=</span> <span class="n">constructor</span><span class="p">;</span>
@ -292,7 +287,7 @@ loading the output.</p>
</pre></div>
</div>
<p>First we get the <em>Color</em> from the node. Then we convert it to a string with the
HTML-like format we&#8217;ve used before. Finally, we use the <em>representScalar()</em>
CSS-like format we&#8217;ve used before. Finally, we use the <em>representScalar()</em>
method of <em>Representer</em> to get a scalar node ready for output.
There are corresponding <em>representMapping()</em> and <em>representSequence()</em> methods
as well, with examples in the
@ -380,7 +375,7 @@ directory of the D:YAML package.</p>
</div>
<div class="footer">
&copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov.
Last updated on Oct 15, 2011.
Last updated on Oct 17, 2011.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7.
</div>
</body>

View file

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

View file

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

View file

@ -20,8 +20,9 @@ Constructor
D:YAML uses the `Constructor <../api/dyaml.constructor.html>`_ class to process
each node to hold data type corresponding to its tag. *Constructor* stores a
function for each supported tag to process it. These functions are supplied by
the user using the *addConstructor()* method. *Constructor* is then passed to
*Loader*, which parses YAML input.
the user using the *addConstructorXXX()* methods, where *XXX* is *Scalar*,
*Sequence* or *Mapping*. *Constructor* is then passed to *Loader*, which parses
YAML input.
Struct types have no specific requirements for YAML support. Class types should
define the *opEquals()* operator, as this is used in equality comparisons of
@ -41,17 +42,20 @@ following struct:
}
First, we need a function to construct our data type. It must take two *Mark*
structs, which store position of the node in the file, and either a *string*, an
array of *Node* or of *Node.Pair*, depending on whether we're constructing our
value from a scalar, sequence, or mapping, respectively. In this tutorial, we
have functions to construct a color from a scalar, using HTML-like format,
RRGGBB, or from a mapping, where we use the following format:
{r:RRR, g:GGG, b:BBB} . Code of these functions:
structs, which store position of the node in the file, and a reference to *Node*
to construct from. The node is guaranteed to contain either a *string*, an array
of *Node* or of *Node.Pair*, depending on whether we're constructing our value
from a scalar, sequence, or mapping, respectively. 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
Color constructColorScalar(Mark start, Mark end, string value)
Color constructColorScalar(Mark start, Mark end, ref Node node)
{
string value = node.get!string;
if(value.length != 6)
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
@ -82,30 +86,21 @@ RRGGBB, or from a mapping, where we use the following format:
return result;
}
Color constructColorMapping(Mark start, Mark end, Node.Pair[] pairs)
Color constructColorMapping(Mark start, Mark end, ref Node node)
{
int r, g, b;
r = g = b = -1;
bool error = pairs.length != 3;
int r,g,b;
bool error = false;
foreach(ref pair; pairs)
//Might throw if a value is missing or is not an integer.
try
{
//Key might not be a string, and value might not be an int,
//so we need to check for that
try
{
switch(pair.key.get!string)
{
case "r": r = pair.value.get!int; break;
case "g": g = pair.value.get!int; break;
case "b": b = pair.value.get!int; break;
default: error = true;
}
}
catch(NodeException e)
{
error = true;
}
r = node["r"].get!int;
g = node["g"].get!int;
b = node["b"].get!int;
}
catch(NodeException e)
{
error = true;
}
if(error || r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255)
@ -146,8 +141,8 @@ Finally, the code to put it all together:
{
auto constructor = new Constructor;
//both functions handle the same tag, but one handles scalar, one mapping.
constructor.addConstructor("!color", &constructColorScalar);
constructor.addConstructor("!color-mapping", &constructColorMapping);
constructor.addConstructorScalar("!color", &constructColorScalar);
constructor.addConstructorMapping("!color-mapping", &constructColorMapping);
auto loader = Loader("input.yaml");
loader.constructor = constructor;
@ -284,7 +279,7 @@ With the following code, we will add support for dumping the our Color type.
}
First we get the *Color* from the node. Then we convert it to a string with the
HTML-like format we've used before. Finally, we use the *representScalar()*
CSS-like format we've used before. Finally, we use the *representScalar()*
method of *Representer* to get a scalar node ready for output.
There are corresponding *representMapping()* and *representSequence()* methods
as well, with examples in the

View file

@ -68,11 +68,11 @@ final class Constructor
{
private:
///Constructor functions from scalars.
Node.Value delegate(Mark, Mark, string) [Tag] fromScalar_;
Node.Value delegate(Mark, Mark, ref Node)[Tag] fromScalar_;
///Constructor functions from sequences.
Node.Value delegate(Mark, Mark, Node[]) [Tag] fromSequence_;
Node.Value delegate(Mark, Mark, ref Node)[Tag] fromSequence_;
///Constructor functions from mappings.
Node.Value delegate (Mark, Mark, Node.Pair[])[Tag] fromMapping_;
Node.Value delegate(Mark, Mark, ref Node)[Tag] fromMapping_;
public:
/**
@ -85,26 +85,25 @@ final class Constructor
*/
this(in bool defaultConstructors = true)
{
if(defaultConstructors)
{
addConstructor("tag:yaml.org,2002:null", &constructNull);
addConstructor("tag:yaml.org,2002:bool", &constructBool);
addConstructor("tag:yaml.org,2002:int", &constructLong);
addConstructor("tag:yaml.org,2002:float", &constructReal);
addConstructor("tag:yaml.org,2002:binary", &constructBinary);
addConstructor("tag:yaml.org,2002:timestamp", &constructTimestamp);
addConstructor("tag:yaml.org,2002:str", &constructString);
if(!defaultConstructors){return;}
///In a mapping, the default value is kept as an entry with the '=' key.
addConstructor("tag:yaml.org,2002:value", &constructString);
addConstructorScalar("tag:yaml.org,2002:null", &constructNull);
addConstructorScalar("tag:yaml.org,2002:bool", &constructBool);
addConstructorScalar("tag:yaml.org,2002:int", &constructLong);
addConstructorScalar("tag:yaml.org,2002:float", &constructReal);
addConstructorScalar("tag:yaml.org,2002:binary", &constructBinary);
addConstructorScalar("tag:yaml.org,2002:timestamp", &constructTimestamp);
addConstructorScalar("tag:yaml.org,2002:str", &constructString);
addConstructor("tag:yaml.org,2002:omap", &constructOrderedMap);
addConstructor("tag:yaml.org,2002:pairs", &constructPairs);
addConstructor("tag:yaml.org,2002:set", &constructSet);
addConstructor("tag:yaml.org,2002:seq", &constructSequence);
addConstructor("tag:yaml.org,2002:map", &constructMap);
addConstructor("tag:yaml.org,2002:merge", &constructMerge);
}
///In a mapping, the default value is kept as an entry with the '=' key.
addConstructorScalar("tag:yaml.org,2002:value", &constructString);
addConstructorSequence("tag:yaml.org,2002:omap", &constructOrderedMap);
addConstructorSequence("tag:yaml.org,2002:pairs", &constructPairs);
addConstructorMapping("tag:yaml.org,2002:set", &constructSet);
addConstructorSequence("tag:yaml.org,2002:seq", &constructSequence);
addConstructorMapping("tag:yaml.org,2002:map", &constructMap);
addConstructorScalar("tag:yaml.org,2002:merge", &constructMerge);
}
///Destroy the constructor.
@ -119,139 +118,129 @@ final class Constructor
}
/**
* Add a constructor function.
* Add a constructor function from scalar.
*
* The function passed must two Marks (determining start and end positions of
* the node in file) and either a string (if constructing from scalar),
* an array of Nodes (from sequence) or an array of Node.Pair (from mapping).
* The value returned by this function will be stored in the resulring node.
* The function must take two Marks (start and end positions of
* the node in file) and a reference to Node to construct from.
* The node contains a string for scalars, Node[] for sequences and
* Node.Pair[] for mappings.
* The value returned by this function will be stored in the resulting node.
*
* Only one constructor function can be set for one tag.
*
* Params: tag = Tag for the function to handle.
* ctor = Constructor function.
*/
void addConstructor(T, U)(in string tag, T function(Mark, Mark, U) ctor)
if(is(U == string) || is(U == Node[]) || is(U == Node.Pair[]))
void addConstructorScalar(T)(in string tag, T function(Mark, Mark, ref Node) ctor)
{
Node.Value delegate(Mark, Mark, U) deleg;
const t = Tag(tag);
auto deleg = addConstructor!T(t, ctor);
(*delegates!string)[t] = deleg;
}
//Type that natively fits into Node.Value.
static if(Node.Value.allowed!T)
{
deleg = (Mark s, Mark e, U p){return Node.Value(ctor(s,e,p));};
}
//User defined type.
else
{
deleg = (Mark s, Mark e, U p){return Node.userValue(ctor(s,e,p));};
}
/**
* Add a constructor function from sequence.
*
* See_Also: addConstructorScalar
*/
void addConstructorSequence(T)(in string tag, T function(Mark, Mark, ref Node) ctor)
{
const t = Tag(tag);
auto deleg = addConstructor!T(t, ctor);
(*delegates!(Node[]))[t] = deleg;
}
const Tag t = Tag(tag);
assert((t in fromScalar_) is null &&
(t in fromSequence_) is null &&
(t in fromMapping_) is null,
"Constructor function for tag " ~ tag ~ " is already "
"specified. Can't specify another one.");
static if(is(U == string))
{
fromScalar_[t] = deleg;
}
else static if(is(U == Node[]))
{
fromSequence_[t] = deleg;
}
else static if(is(U == Node.Pair[]))
{
fromMapping_[t] = deleg;
}
/**
* Add a constructor function from a mapping.
*
* See_Also: addConstructorScalar
*/
void addConstructorMapping(T)(in string tag, T function(Mark, Mark, ref Node) ctor)
{
const t = Tag(tag);
auto deleg = addConstructor!T(t, ctor);
(*delegates!(Node.Pair[]))[t] = deleg;
}
package:
/*
* Construct a node from scalar.
* Construct a node.
*
* Params: start = Start position of the node.
* end = End position of the node.
* tag = Tag (data type) of the node.
* value = String value of the node.
* value = Value to construct node from (string, nodes or pairs).
*
* Returns: Constructed node.
*/
Node node(in Mark start, in Mark end, in Tag tag, string value) const
Node node(T)(in Mark start, in Mark end, in Tag tag, T value)
if(is(T : string) || is(T == Node[]) || is(T == Node.Pair[]))
{
enforce((tag in fromScalar_) !is null,
new ConstructorException("Could not determine a constructor from "
"scalar for tag " ~ tag.get(), start, end));
return Node.rawNode(fromScalar_[tag](start, end, value), start, tag);
enforce((tag in *delegates!T) !is null,
new ConstructorException("Could not determine a constructor for tag "
~ tag.get(), start, end));
Node node = Node(value);
return Node.rawNode((*delegates!T)[tag](start, end, node), start, tag);
}
/*
* Construct a node from sequence.
private:
/*
* Add a constructor function.
*
* Params: start = Start position of the node.
* end = End position of the node.
* tag = Tag (data type) of the node.
* value = Sequence to construct node from.
*
* Returns: Constructed node.
*/
Node node(in Mark start, in Mark end, in Tag tag, Node[] value) const
* Params: tag = Tag for the function to handle.
* ctor = Constructor function.
*/
auto addConstructor(T)(in Tag tag, T function(Mark, Mark, ref Node) ctor)
{
enforce((tag in fromSequence_) !is null,
new ConstructorException("Could not determine a constructor from "
"sequence for tag " ~ tag.get(), start, end));
return Node.rawNode(fromSequence_[tag](start, end, value), start, tag);
assert((tag in fromScalar_) is null &&
(tag in fromSequence_) is null &&
(tag in fromMapping_) is null,
"Constructor function for tag " ~ tag.get ~ " is already "
"specified. Can't specify another one.");
return (Mark s, Mark e, ref Node n)
{
static if(Node.Value.allowed!T){return Node.Value(ctor(s,e,n));}
else {return Node.userValue(ctor(s,e,n));}
};
}
/*
* Construct a node from mapping.
*
* Params: start = Start position of the node.
* end = End position of the node.
* tag = Tag (data type) of the node.
* value = Mapping to construct node from.
*
* Returns: Constructed node.
*/
Node node(in Mark start, in Mark end, in Tag tag, Node.Pair[] value) const
//Get the array of constructor functions for scalar, sequence or mapping.
auto delegates(T)()
{
enforce((tag in fromMapping_) !is null,
new ConstructorException("Could not determine a constructor from "
"mapping for tag " ~ tag.get(), start, end));
return Node.rawNode(fromMapping_[tag](start, end, value), start, tag);
static if(is(T : string)) {return &fromScalar_;}
else static if(is(T : Node[])) {return &fromSequence_;}
else static if(is(T : Node.Pair[])){return &fromMapping_;}
else static assert(false);
}
}
///Construct a null node.
YAMLNull constructNull(Mark start, Mark end, string value)
YAMLNull constructNull(Mark start, Mark end, ref Node node)
{
return YAMLNull();
}
///Construct a merge node - a node that merges another node into a mapping.
YAMLMerge constructMerge(Mark start, Mark end, string value)
YAMLMerge constructMerge(Mark start, Mark end, ref Node node)
{
return YAMLMerge();
}
///Construct a boolean node.
bool constructBool(Mark start, Mark end, string value)
bool constructBool(Mark start, Mark end, ref Node node)
{
value = value.toLower();
string value = node.get!string().toLower();
if(["yes", "true", "on"].canFind(value)) {return true;}
if(["no", "false", "off"].canFind(value)){return false;}
throw new ConstructorException("Unable to parse boolean value: " ~ value, start, end);
}
///Construct an integer (long) node.
long constructLong(Mark start, Mark end, string value)
long constructLong(Mark start, Mark end, ref Node node)
{
value = value.replace("_", "");
string value = node.get!string().replace("_", "");
const char c = value[0];
const long sign = c != '-' ? 1 : -1;
if(c == '-' || c == '+')
@ -299,7 +288,7 @@ unittest
{
long getLong(string str)
{
return constructLong(Mark(), Mark(), str);
return constructLong(Mark(), Mark(), Node(str));
}
string canonical = "685230";
@ -318,9 +307,9 @@ unittest
}
///Construct a floating point (real) node.
real constructReal(Mark start, Mark end, string value)
real constructReal(Mark start, Mark end, ref Node node)
{
value = value.replace("_", "").toLower();
string value = node.get!string().replace("_", "").toLower();
const char c = value[0];
const real sign = c != '-' ? 1.0 : -1.0;
if(c == '-' || c == '+')
@ -369,7 +358,7 @@ unittest
real getReal(string str)
{
return constructReal(Mark(), Mark(), str);
return constructReal(Mark(), Mark(), Node(str));
}
string canonical = "6.8523015e+5";
@ -388,8 +377,9 @@ unittest
}
///Construct a binary (base64) node.
ubyte[] constructBinary(Mark start, Mark end, string value)
ubyte[] constructBinary(Mark start, Mark end, ref Node node)
{
string value = node.get!string;
//For an unknown reason, this must be nested to work (compiler bug?).
try
{
@ -411,13 +401,15 @@ unittest
char[] buffer;
buffer.length = 256;
string input = cast(string)Base64.encode(test, buffer);
auto value = constructBinary(Mark(), Mark(), input);
auto value = constructBinary(Mark(), Mark(), Node(input));
assert(value == test);
}
///Construct a timestamp (SysTime) node.
SysTime constructTimestamp(Mark start, Mark end, string value)
SysTime constructTimestamp(Mark start, Mark end, ref Node node)
{
string value = node.get!string;
immutable YMDRegexp = regex("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)");
immutable HMSRegexp = regex("^[Tt \t]+([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(\\.[0-9]*)?");
immutable TZRegexp = regex("^[ \t]*Z|([-+][0-9][0-9]?)(:[0-9][0-9])?");
@ -492,7 +484,7 @@ unittest
string timestamp(string value)
{
return constructTimestamp(Mark(), Mark(), value).toISOString();
return constructTimestamp(Mark(), Mark(), Node(value)).toISOString();
}
string canonical = "2001-12-15T02:59:43.1Z";
@ -515,9 +507,9 @@ unittest
}
///Construct a string node.
string constructString(Mark start, Mark end, string value)
string constructString(Mark start, Mark end, ref Node node)
{
return value;
return node.get!string;
}
///Convert a sequence of single-element mappings into a sequence of pairs.
@ -539,9 +531,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.
Node.Pair[] constructOrderedMap(Mark start, Mark end, Node[] nodes)
Node.Pair[] constructOrderedMap(Mark start, Mark end, ref Node node)
{
auto pairs = getPairs("ordered map", start, end, nodes);
auto pairs = getPairs("ordered map", start, end, node.get!(Node[]));
//TODO: the map here should be replaced with something with deterministic
//memory allocation if possible.
@ -588,7 +580,7 @@ unittest
bool hasDuplicates(Node[] nodes)
{
return null !is collectException(constructOrderedMap(Mark(), Mark(), nodes));
return null !is collectException(constructOrderedMap(Mark(), Mark(), Node(nodes)));
}
assert(hasDuplicates(alternateTypes(8) ~ alternateTypes(2)));
@ -600,14 +592,16 @@ unittest
}
///Construct a pairs (ordered sequence of key: value pairs allowing duplicates) node.
Node.Pair[] constructPairs(Mark start, Mark end, Node[] nodes)
Node.Pair[] constructPairs(Mark start, Mark end, ref Node node)
{
return getPairs("pairs", start, end, nodes);
return getPairs("pairs", start, end, node.get!(Node[]));
}
///Construct a set node.
Node[] constructSet(Mark start, Mark end, Node.Pair[] pairs)
Node[] constructSet(Mark start, Mark end, ref Node node)
{
auto pairs = node.get!(Node.Pair[]);
//In future, the map here should be replaced with something with deterministic
//memory allocation if possible.
//Detect duplicates.
@ -658,24 +652,25 @@ unittest
}
assert(null !is collectException
(constructSet(Mark(), Mark(), DuplicatesShort.dup)));
(constructSet(Mark(), Mark(), Node(DuplicatesShort.dup))));
assert(null is collectException
(constructSet(Mark(), Mark(), noDuplicatesShort.dup)));
(constructSet(Mark(), Mark(), Node(noDuplicatesShort.dup))));
assert(null !is collectException
(constructSet(Mark(), Mark(), DuplicatesLong.dup)));
(constructSet(Mark(), Mark(), Node(DuplicatesLong.dup))));
assert(null is collectException
(constructSet(Mark(), Mark(), noDuplicatesLong.dup)));
(constructSet(Mark(), Mark(), Node(noDuplicatesLong.dup))));
}
///Construct a sequence (array) node.
Node[] constructSequence(Mark start, Mark end, Node[] nodes)
Node[] constructSequence(Mark start, Mark end, ref Node node)
{
return nodes;
return node.get!(Node[]);
}
///Construct an unordered map (unordered set of key: value _pairs without duplicates) node.
Node.Pair[] constructMap(Mark start, Mark end, Node.Pair[] pairs)
Node.Pair[] constructMap(Mark start, Mark end, ref Node node)
{
auto pairs = node.get!(Node.Pair[]);
//TODO: the map here should be replaced with something with deterministic
//memory allocation if possible.
//Detect duplicates.

View file

@ -251,11 +251,12 @@ struct Node
{
tag_ = Tag(tag);
//Construction from raw node or pair array.
static if(is(T == Node) || is(T == Node.Pair))
{
value_ = Value(array);
}
//Need to handle byte buffers separately
//Need to handle byte buffers separately.
else static if(is(T == byte) || is(T == ubyte))
{
value_ = Value(cast(ubyte[]) array);

View file

@ -10,70 +10,63 @@ struct Color
ubyte blue;
}
Color constructColorScalar(Mark start, Mark end, string value)
Color constructColorScalar(Mark start, Mark end, ref Node node)
{
if(value.length != 6)
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
}
//We don't need to check for uppercase chars this way.
value = value.toLower();
string value = node.get!string;
//Get value of a hex digit.
uint hex(char c)
{
if(!std.ascii.isHexDigit(c))
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
}
if(value.length != 6)
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
}
//We don't need to check for uppercase chars this way.
value = value.toLower();
if(std.ascii.isDigit(c))
{
return c - '0';
}
return c - 'a' + 10;
}
//Get value of a hex digit.
uint hex(char c)
{
if(!std.ascii.isHexDigit(c))
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
}
Color result;
result.red = cast(ubyte)(16 * hex(value[0]) + hex(value[1]));
result.green = cast(ubyte)(16 * hex(value[2]) + hex(value[3]));
result.blue = cast(ubyte)(16 * hex(value[4]) + hex(value[5]));
if(std.ascii.isDigit(c))
{
return c - '0';
}
return c - 'a' + 10;
}
return result;
Color result;
result.red = cast(ubyte)(16 * hex(value[0]) + hex(value[1]));
result.green = cast(ubyte)(16 * hex(value[2]) + hex(value[3]));
result.blue = cast(ubyte)(16 * hex(value[4]) + hex(value[5]));
return result;
}
Color constructColorMapping(Mark start, Mark end, Node.Pair[] pairs)
Color constructColorMapping(Mark start, Mark end, ref Node node)
{
int r, g, b;
r = g = b = -1;
bool error = pairs.length != 3;
int r,g,b;
bool error = false;
foreach(ref pair; pairs)
{
//Key might not be a string, and value might not be an int,
//so we need to check for that
try
{
switch(pair.key.get!string)
{
case "r": r = pair.value.get!int; break;
case "g": g = pair.value.get!int; break;
case "b": b = pair.value.get!int; break;
default: error = true;
}
}
catch(NodeException e)
{
error = true;
}
}
//Might throw if a value is missing or is not an integer.
try
{
r = node["r"].get!int;
g = node["g"].get!int;
b = node["b"].get!int;
}
catch(NodeException e)
{
error = true;
}
if(error || r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255)
{
throw new ConstructorException("Invalid color", start, end);
}
if(error || r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255)
{
throw new ConstructorException("Invalid color", start, end);
}
return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b);
return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b);
}
void main()
@ -85,8 +78,8 @@ void main()
{
auto constructor = new Constructor;
//both functions handle the same tag, but one handles scalar, one mapping.
constructor.addConstructor("!color", &constructColorScalar);
constructor.addConstructor("!color-mapping", &constructColorMapping);
constructor.addConstructorScalar("!color", &constructColorScalar);
constructor.addConstructorMapping("!color-mapping", &constructColorMapping);
auto loader = Loader("input.yaml");
loader.constructor = constructor;

View file

@ -10,70 +10,63 @@ struct Color
ubyte blue;
}
Color constructColorScalar(Mark start, Mark end, string value)
Color constructColorScalar(Mark start, Mark end, ref Node node)
{
if(value.length != 6)
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
}
//We don't need to check for uppercase chars this way.
value = value.toLower();
string value = node.get!string;
//Get value of a hex digit.
uint hex(char c)
{
if(!std.ascii.isHexDigit(c))
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
}
if(value.length != 6)
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
}
//We don't need to check for uppercase chars this way.
value = value.toLower();
if(std.ascii.isDigit(c))
{
return c - '0';
}
return c - 'a' + 10;
}
//Get value of a hex digit.
uint hex(char c)
{
if(!std.ascii.isHexDigit(c))
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
}
Color result;
result.red = cast(ubyte)(16 * hex(value[0]) + hex(value[1]));
result.green = cast(ubyte)(16 * hex(value[2]) + hex(value[3]));
result.blue = cast(ubyte)(16 * hex(value[4]) + hex(value[5]));
if(std.ascii.isDigit(c))
{
return c - '0';
}
return c - 'a' + 10;
}
return result;
Color result;
result.red = cast(ubyte)(16 * hex(value[0]) + hex(value[1]));
result.green = cast(ubyte)(16 * hex(value[2]) + hex(value[3]));
result.blue = cast(ubyte)(16 * hex(value[4]) + hex(value[5]));
return result;
}
Color constructColorMapping(Mark start, Mark end, Node.Pair[] pairs)
Color constructColorMapping(Mark start, Mark end, ref Node node)
{
int r, g, b;
r = g = b = -1;
bool error = pairs.length != 3;
int r,g,b;
bool error = false;
foreach(ref pair; pairs)
{
//Key might not be a string, and value might not be an int,
//so we need to check for that
try
{
switch(pair.key.get!string)
{
case "r": r = pair.value.get!int; break;
case "g": g = pair.value.get!int; break;
case "b": b = pair.value.get!int; break;
default: error = true;
}
}
catch(NodeException e)
{
error = true;
}
}
//Might throw if a value is missing or is not an integer.
try
{
r = node["r"].get!int;
g = node["g"].get!int;
b = node["b"].get!int;
}
catch(NodeException e)
{
error = true;
}
if(error || r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255)
{
throw new ConstructorException("Invalid color", start, end);
}
if(error || r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255)
{
throw new ConstructorException("Invalid color", start, end);
}
return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b);
return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b);
}
void main()
@ -85,8 +78,8 @@ void main()
{
auto constructor = new Constructor;
//both functions handle the same tag, but one handles scalar, one mapping.
constructor.addConstructor("!color", &constructColorScalar);
constructor.addConstructor("!color-mapping", &constructColorMapping);
constructor.addConstructorScalar("!color", &constructColorScalar);
constructor.addConstructorMapping("!color-mapping", &constructColorMapping);
auto resolver = new Resolver;
resolver.addImplicitResolver("!color", std.regex.regex("[0-9a-fA-F]{6}"),

View file

@ -1,6 +1,4 @@
---
- !tag1
x: 1
- !tag1
x: 1
'y': 2

View file

@ -88,8 +88,7 @@ Node[] constructBool()
Node[] constructCustom()
{
return [Node([Node(new TestClass(1, 0, 0)),
Node(new TestClass(1, 2, 3)),
return [Node([Node(new TestClass(1, 2, 3)),
Node(TestStruct(10))])];
}
@ -338,21 +337,17 @@ struct TestStruct
}
///Constructor function for TestClass.
TestClass constructClass(Mark start, Mark end, Node.Pair[] pairs)
TestClass constructClass(Mark start, Mark end, ref Node node)
{
int x, y, z;
foreach(ref pair; pairs)
try
{
switch(pair.key.get!string)
{
case "x": x = pair.value.get!int; break;
case "y": y = pair.value.get!int; break;
case "z": z = pair.value.get!int; break;
default: break;
}
return new TestClass(node["x"].get!int, node["y"].get!int, node["z"].get!int);
}
catch(NodeException e)
{
throw new ConstructorException("Error constructing TestClass (missing data members?) "
~ e.msg, start, end);
}
return new TestClass(x, y, z);
}
Node representClass(ref Node node, Representer representer)
@ -367,9 +362,9 @@ Node representClass(ref Node node, Representer representer)
}
///Constructor function for TestStruct.
TestStruct constructStruct(Mark start, Mark end, string value)
TestStruct constructStruct(Mark start, Mark end, ref Node node)
{
return TestStruct(to!int(value));
return TestStruct(to!int(node.get!string));
}
///Representer function for TestStruct.
@ -395,8 +390,8 @@ void testConstructor(bool verbose, string dataFilename, string codeDummy)
new Exception("Unimplemented constructor test: " ~ base));
auto constructor = new Constructor;
constructor.addConstructor("!tag1", &constructClass);
constructor.addConstructor("!tag2", &constructStruct);
constructor.addConstructorMapping("!tag1", &constructClass);
constructor.addConstructorScalar("!tag2", &constructStruct);
auto loader = Loader(dataFilename);
loader.constructor = constructor;

View file

@ -58,8 +58,8 @@ void testRepresenterTypes(bool verbose, string codeFilename)
output = cast(string)emitStream.data;
auto loadStream = new MemoryStream(emitStream.data);
auto constructor = new Constructor;
constructor.addConstructor("!tag1", &constructClass);
constructor.addConstructor("!tag2", &constructStruct);
constructor.addConstructorMapping("!tag1", &constructClass);
constructor.addConstructorScalar("!tag2", &constructStruct);
auto loader = Loader(loadStream);
loader.name = "TEST";