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 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 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 function for each supported tag to process it. These functions are supplied by
the user using the *addConstructor()* method. *Constructor* is then passed to the user using the *addConstructorXXX()* methods, where *XXX* is *Scalar*,
*Loader*, which parses YAML input. *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 Struct types have no specific requirements for YAML support. Class types should
define the *opEquals()* operator, as this is used in equality comparisons of define the *opEquals()* operator, 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* 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 structs, which store position of the node in the file, and a reference to *Node*
array of *Node* or of *Node.Pair*, depending on whether we're constructing our to construct from. The node is guaranteed to contain either a *string*, an array
value from a scalar, sequence, or mapping, respectively. In this tutorial, we of *Node* or of *Node.Pair*, depending on whether we're constructing our value
have functions to construct a color from a scalar, using HTML-like format, from a scalar, sequence, or mapping, respectively. In this tutorial, we have
RRGGBB, or from a mapping, where we use the following format: functions to construct a color from a scalar, using CSS-like format, RRGGBB, or
{r:RRR, g:GGG, b:BBB} . Code of these functions: from a mapping, where we use the following format: {r:RRR, g:GGG, b:BBB} . Code
of these functions:
.. code-block:: d .. code-block:: d
Color constructColorScalar(Mark start, Mark end, string value) Color constructColorScalar(Mark start, Mark end, ref Node node)
{ {
string value = node.get!string;
if(value.length != 6) if(value.length != 6)
{ {
throw new ConstructorException("Invalid color: " ~ value, start, end); 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; return result;
} }
Color constructColorMapping(Mark start, Mark end, Node.Pair[] pairs) Color constructColorMapping(Mark start, Mark end, ref Node node)
{ {
int r, g, b; int r,g,b;
r = g = b = -1; bool error = false;
bool error = pairs.length != 3;
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, r = node["r"].get!int;
//so we need to check for that g = node["g"].get!int;
try b = node["b"].get!int;
{ }
switch(pair.key.get!string) catch(NodeException e)
{ {
case "r": r = pair.value.get!int; break; error = true;
case "g": g = pair.value.get!int; break;
case "b": b = pair.value.get!int; break;
default: error = true;
}
}
catch(NodeException e)
{
error = true;
}
} }
if(error || r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) 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; auto constructor = new Constructor;
//both functions handle the same tag, but one handles scalar, one mapping. //both functions handle the same tag, but one handles scalar, one mapping.
constructor.addConstructor("!color", &constructColorScalar); constructor.addConstructorScalar("!color", &constructColorScalar);
constructor.addConstructor("!color-mapping", &constructColorMapping); constructor.addConstructorMapping("!color-mapping", &constructColorMapping);
auto loader = Loader("input.yaml"); auto loader = Loader("input.yaml");
loader.constructor = constructor; 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 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. method of *Representer* to get a scalar node ready for output.
There are corresponding *representMapping()* and *representSequence()* methods There are corresponding *representMapping()* and *representSequence()* methods
as well, with examples in the as well, with examples in the

View file

@ -94,14 +94,15 @@
</table></div> </table></div>
</dd> </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> </dt>
<dd><p>Add a constructor function. <dd><p>Add a constructor function from scalar.
</p> </p>
<p>The function passed must two Marks (determining start and end positions of <p>The function must take two Marks (start and end positions of
the node in file) and either a string (if constructing from scalar), the node in file) and a reference to Node to construct from.
an array of Nodes (from sequence) or an array of Node.Pair (from mapping). The node contains a string for scalars, Node[] for sequences and
The value returned by this function will be stored in the resulring node. Node.Pair[] for mappings.
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.
@ -113,47 +114,61 @@
<td valign=top>Constructor function.</td></tr> <td valign=top>Constructor function.</td></tr>
</table></div> </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> </dd>
</dl> </dl>
</dd> </dd>
<dt class="d_decl">YAMLNull <a name="constructNull"></a><span class="ddoc_psymbol">constructNull</span>(Mark <b>start</b>, Mark <b>end</b>, 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> </dt>
<dd><p>Construct a <b>null</b> node.</p> <dd><p>Construct a <b>null</b> <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">YAMLMerge <a name="constructMerge"></a><span class="ddoc_psymbol">constructMerge</span>(Mark <b>start</b>, Mark <b>end</b>, 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> </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> </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> </dt>
<dd><p>Construct a boolean node.</p> <dd><p>Construct a boolean <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">long <a name="constructLong"></a><span class="ddoc_psymbol">constructLong</span>(Mark <b>start</b>, Mark <b>end</b>, 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> </dt>
<dd><p>Construct an integer (long) node.</p> <dd><p>Construct an integer (long) <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">real <a name="constructReal"></a><span class="ddoc_psymbol">constructReal</span>(Mark <b>start</b>, Mark <b>end</b>, 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> </dt>
<dd><p>Construct a floating point (real) node.</p> <dd><p>Construct a floating point (real) <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">ubyte[] <a name="constructBinary"></a><span class="ddoc_psymbol">constructBinary</span>(Mark <b>start</b>, Mark <b>end</b>, 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> </dt>
<dd><p>Construct a binary (base64) node.</p> <dd><p>Construct a binary (base64) <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">SysTime <a name="constructTimestamp"></a><span class="ddoc_psymbol">constructTimestamp</span>(Mark <b>start</b>, Mark <b>end</b>, 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> </dt>
<dd><p>Construct a timestamp (SysTime) node.</p> <dd><p>Construct a timestamp (SysTime) <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">string <a name="constructString"></a><span class="ddoc_psymbol">constructString</span>(Mark <b>start</b>, Mark <b>end</b>, 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> </dt>
<dd><p>Construct a string node.</p> <dd><p>Construct a string <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">Pair[] <a name="getPairs"></a><span class="ddoc_psymbol">getPairs</span>(string <b>type</b>, Mark <b>start</b>, Mark <b>end</b>, Node[] <b>nodes</b>); <dt class="d_decl">Pair[] <a name="getPairs"></a><span class="ddoc_psymbol">getPairs</span>(string <b>type</b>, 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><p>Convert a sequence of single-element mappings into a sequence of pairs.</p>
</dd> </dd>
<dt class="d_decl">Pair[] <a name="constructOrderedMap"></a><span class="ddoc_psymbol">constructOrderedMap</span>(Mark <b>start</b>, Mark <b>end</b>, 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> </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> </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> </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> </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> </dt>
<dd><p>Construct a set node.</p> <dd><p>Construct a set <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">Node[] <a name="constructSequence"></a><span class="ddoc_psymbol">constructSequence</span>(Mark <b>start</b>, Mark <b>end</b>, 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> </dt>
<dd><p>Construct a sequence (array) node.</p> <dd><p>Construct a sequence (array) <b>node</b>.</p>
</dd> </dd>
<dt class="d_decl">Pair[] <a name="constructMap"></a><span class="ddoc_psymbol">constructMap</span>(Mark <b>start</b>, Mark <b>end</b>, 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> </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> </dd>
</dl> </dl>

View file

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

View file

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

View file

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

File diff suppressed because one or more lines are too long

View file

@ -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 <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 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 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 the user using the <em>addConstructorXXX()</em> methods, where <em>XXX</em> is <em>Scalar</em>,
<em>Loader</em>, which parses YAML input.</p> <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 <p>Struct types have no specific requirements for YAML support. Class types should
define the <em>opEquals()</em> operator, as this is used in equality comparisons of define the <em>opEquals()</em> operator, as this is used in equality comparisons of
nodes. Default class <em>opEquals()</em> compares references, which means two identical nodes. Default class <em>opEquals()</em> compares references, which means two identical
@ -77,14 +78,17 @@ following struct:</p>
</pre></div> </pre></div>
</div> </div>
<p>First, we need a function to construct our data type. It must take two <em>Mark</em> <p>First, we need a function to construct our data type. It must take two <em>Mark</em>
structs, which store position of the node in the file, and either a <em>string</em>, an structs, which store position of the node in the file, and a reference to <em>Node</em>
array of <em>Node</em> or of <em>Node.Pair</em>, depending on whether we&#8217;re constructing our to construct from. The node is guaranteed to contain either a <em>string</em>, an array
value from a scalar, sequence, or mapping, respectively. In this tutorial, we of <em>Node</em> or of <em>Node.Pair</em>, depending on whether we&#8217;re constructing our value
have functions to construct a color from a scalar, using HTML-like format, from a scalar, sequence, or mapping, respectively. In this tutorial, we have
RRGGBB, or from a mapping, where we use the following format: functions to construct a color from a scalar, using CSS-like format, RRGGBB, or
{r:RRR, g:GGG, b:BBB} . Code of these functions:</p> from a mapping, where we use the following format: {r:RRR, g:GGG, b:BBB} . Code
<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> 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="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="k">if</span><span class="p">(</span><span class="n">value</span><span class="p">.</span><span class="n">length</span> <span class="p">!=</span> <span class="mi">6</span><span class="p">)</span>
<span class="p">{</span> <span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="n">ConstructorException</span><span class="p">(</span><span class="s">&quot;Invalid color: &quot;</span> <span class="p">~</span> <span class="n">value</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">);</span> <span class="k">throw</span> <span class="k">new</span> <span class="n">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="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span> <span class="p">}</span>
<span class="n">Color</span> <span class="n">constructColorMapping</span><span class="p">(</span><span class="n">Mark</span> <span class="n">start</span><span class="p">,</span> <span class="n">Mark</span> <span class="n">end</span><span class="p">,</span> <span class="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="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">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="kc">false</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="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="p">{</span>
<span class="c1">//Key might not be a string, and value might not be an int,</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="c1">//so we need to check for that</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="k">try</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="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="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="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="n">error</span> <span class="p">=</span> <span class="kc">true</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="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> <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="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="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="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">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">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">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="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> <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> </pre></div>
</div> </div>
<p>First we get the <em>Color</em> from the node. Then we convert it to a string with the <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. method of <em>Representer</em> to get a scalar node ready for output.
There are corresponding <em>representMapping()</em> and <em>representSequence()</em> methods There are corresponding <em>representMapping()</em> and <em>representSequence()</em> methods
as well, with examples in the as well, with examples in the
@ -380,7 +375,7 @@ directory of the D:YAML package.</p>
</div> </div>
<div class="footer"> <div class="footer">
&copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov. &copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov.
Last updated on Oct 15, 2011. Last updated on Oct 17, 2011.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7. Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7.
</div> </div>
</body> </body>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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