Implemented the resolver unittest.
Changed Loader API to be in line with Dumper, and updated examples, tutorials and docs.
This commit is contained in:
parent
934df763ad
commit
34b11405d4
|
@ -9,7 +9,7 @@ are caused by difficulty of implementation of some features, such as multiple
|
|||
Unicode encodings within single stream, and some by unnecessary restrictions or
|
||||
ambiguities in the specification.
|
||||
|
||||
Still, D:YAML tries to be as close to the specification as possible. D:YAML should
|
||||
Still, D:YAML tries to be as close to the specification as possible. It should
|
||||
never load documents with different meaning than according to the specification,
|
||||
and documents that fail to load should be very rare (for instance, very few
|
||||
files use multiple Unicode encodings).
|
||||
|
@ -21,10 +21,6 @@ List of known differences:
|
|||
|
||||
Differences that can cause valid YAML documents not to load:
|
||||
|
||||
* At the moment, all mappings in the internal representation are ordered,
|
||||
and a comparison for equality between equal mappings with differing order
|
||||
will return false. This will be fixed once Phobos has a usable map type or
|
||||
D associative arrays work with variants.
|
||||
* No support for byte order marks and multiple Unicode encodings in a stream.
|
||||
* Plain scalars in flow context cannot contain ``,``, ``:`` and ``?``.
|
||||
This might change with ``:`` in the future.
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
Custom YAML data types
|
||||
======================
|
||||
|
||||
Often you will want to serialize complex data types such as classes. You can use
|
||||
functions to process nodes; e.g. a mapping containing class data members indexed
|
||||
by name. Alternatively, YAML supports custom data types using identifiers called
|
||||
*tags*. That is the topic of this tutorial.
|
||||
Often you might want to serialize complex data types such as classes. You can
|
||||
use functions to process nodes such as a mapping containing class data members
|
||||
indexed by name. Alternatively, YAML supports custom data types using
|
||||
identifiers called *tags*. That is the topic of this tutorial.
|
||||
|
||||
Each YAML node has a tag specifying its type. For instance: strings use the tag
|
||||
``tag:yaml.org,2002:str``. Tags of most default types are *implicitly resolved*
|
||||
|
@ -19,9 +19,9 @@ Constructor
|
|||
|
||||
D:YAML uses the *Constructor* 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 can be supplied by the user using the
|
||||
*addConstructor()* method. *Constructor* is then passed to *Loader*, which will
|
||||
parse YAML input.
|
||||
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.
|
||||
|
||||
We will implement support for an RGB color type. It is implemented as the
|
||||
following struct:
|
||||
|
@ -111,7 +111,7 @@ RRGGBB, or from a mapping, where we use the following format:
|
|||
return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b);
|
||||
}
|
||||
|
||||
Next, we need some YAML code using our new tag. Create a file called input.yaml
|
||||
Next, we need some YAML data using our new tag. Create a file called input.yaml
|
||||
with the following contents:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
@ -144,9 +144,10 @@ Finally, the code to put it all together:
|
|||
constructor.addConstructor("!color", &constructColorScalar);
|
||||
constructor.addConstructor("!color-mapping", &constructColorMapping);
|
||||
|
||||
auto loader = new Loader("input.yaml", constructor, new Resolver);
|
||||
auto loader = Loader("input.yaml");
|
||||
loader.constructor = constructor;
|
||||
|
||||
auto root = loader.loadSingleDocument();
|
||||
auto root = loader.load();
|
||||
|
||||
if(root["scalar-red"].get!Color == red &&
|
||||
root["mapping-red"].get!Color == red &&
|
||||
|
@ -166,10 +167,9 @@ Finally, the code to put it all together:
|
|||
}
|
||||
|
||||
First, we create a *Constructor* and pass functions to handle the ``!color``
|
||||
and ``!color-mapping`` tag. We construct a *Loader* using the *Constructor*.
|
||||
We also need a *Resolver*, but for now we just default-construct it. We then
|
||||
load the YAML document, and finally, read the colors using *get()* method to
|
||||
test if they were loaded as expected.
|
||||
and ``!color-mapping`` tag. We construct a *Loader*m and pass the *Constructor*
|
||||
to it. We then load the YAML document, and finally, read the colors using
|
||||
*get()* method to test if they were loaded as expected.
|
||||
|
||||
You can find the source code for what we've done so far in the
|
||||
``examples/constructor`` directory in the D:YAML package.
|
||||
|
@ -180,8 +180,8 @@ Resolver
|
|||
--------
|
||||
|
||||
Specifying tag for every color value can be tedious. D:YAML can implicitly
|
||||
resolve tag of a scalar using a regular expression. This is how default types,
|
||||
e.g. int, are resolved. We will use the *Resolver* class to add implicit tag
|
||||
resolve scalar tags using regular expressions. This is how default types such as
|
||||
int are resolved. We will use the *Resolver* class to add implicit tag
|
||||
resolution for the Color data type (in its scalar form).
|
||||
|
||||
We use the *addImplicitResolver* method of *Resolver*, passing the tag, regular
|
||||
|
@ -203,11 +203,14 @@ Add this to your code to add implicit resolution of ``!color``.
|
|||
resolver.addImplicitResolver("!color", std.regex.regex("[0-9a-fA-F]{6}",
|
||||
"0123456789abcdefABCDEF"));
|
||||
|
||||
auto loader = new Loader("input.yaml", constructor, resolver);
|
||||
auto loader = Loader("input.yaml");
|
||||
|
||||
loader.constructor = constructor;
|
||||
loader.resolver = resolver;
|
||||
|
||||
//code from the previous example...
|
||||
|
||||
Now, change contents of input.dyaml to this:
|
||||
Now, change contents of input.yaml to this:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
|
|
@ -26,10 +26,8 @@ Download the version of DMD for your operating system and install it.
|
|||
.. note::
|
||||
Other D compilers exist, such as
|
||||
`GDC <http://bitbucket.org/goshawk/gdc/wiki/Home>`_ and
|
||||
`LDC <http://www.dsource.org/projects/ldc/>`_.
|
||||
Setting up with either one of them should be similar to DMD,
|
||||
however, at the moment they are not as up to date as DMD.
|
||||
|
||||
`LDC <http://www.dsource.org/projects/ldc/>`_. Setting up with either one of
|
||||
them should be similar to DMD, but they are not yet as stable as DMD.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Download and compile D:YAML
|
||||
|
@ -84,7 +82,7 @@ into the file:
|
|||
|
||||
void main()
|
||||
{
|
||||
yaml.Node root = yaml.load("input.yaml");
|
||||
Node root = Loader("input.yaml").load();
|
||||
foreach(string word; root["Hello World"])
|
||||
{
|
||||
writeln(word);
|
||||
|
@ -100,22 +98,23 @@ Explanation of the code
|
|||
First, we import the *yaml* module. This is the only module you need to import
|
||||
to use D:YAML - it automatically imports all needed modules.
|
||||
|
||||
Next we load the file using the *yaml.load()* function - this loads the file as
|
||||
Next we load the file using the *Loader.load()* method. *Loader* is the struct
|
||||
used for parsing YAML documents, and *load()* is a method that loads the file as
|
||||
**one** YAML document and throws *YAMLException*, D:YAML exception type, if the
|
||||
file could not be parsed or does not contain exactly one document. Note that we
|
||||
don't do any error checking here in order to keep the example as simple as
|
||||
possible.
|
||||
|
||||
*yaml.Node* represents a node in a YAML document. It can be a sequence (array),
|
||||
*Node* represents a node in a YAML document. It can be a sequence (array),
|
||||
mapping (associative array) or a scalar (value). Here the root node is a
|
||||
mapping, and we use the index operator to get subnodes with keys "Hello World"
|
||||
and "Answer". We iterate over the first, as it is a sequence, and use the
|
||||
*yaml.Node.get()* method on the second to get its value as an integer.
|
||||
*Node.get()* method on the second to get its value as an integer.
|
||||
|
||||
You can iterate over a mapping or sequence as if it was an associative or normal
|
||||
array. If you try to iterate over a scalar, it will throw a *YAMLException*.
|
||||
|
||||
You can iterate over subnodes using yaml.Node as the iterated type, or specify
|
||||
You can iterate over subnodes using *Node* as the iterated type, or specify
|
||||
the type subnodes are expected to have. D:YAML will automatically convert
|
||||
iterated subnodes to that type if possible. Here we specify the *string* type,
|
||||
so we iterate over the "Hello World" sequence as an array of strings. If it is
|
||||
|
@ -123,8 +122,8 @@ not possible to convert to iterated type, a *YAMLException* is thrown. For
|
|||
instance, if we specified *int* here, we would get an error, as "Hello"
|
||||
cannot be converted to an integer.
|
||||
|
||||
The *yaml.Node.get()* method is used to get value of a scalar node as specified
|
||||
type. D:YAML will try to return the scalar as specified type, converting if
|
||||
The *Node.get()* method is used to get value of a scalar node, allowing to
|
||||
specify type. D:YAML will try to return the scalar as this type, converting if
|
||||
needed, throwing *YAMLException* if not possible.
|
||||
|
||||
|
||||
|
@ -135,16 +134,15 @@ Compiling
|
|||
To compile your project, you must give DMD the directories containing import
|
||||
modules and the library. You also need to tell it to link with D:YAML. The import
|
||||
directory should be the D:YAML package directory. You can specify it using the
|
||||
``-I`` option of DMD. The library directory should point to where you put the
|
||||
compiled D:YAML library. On Unix/Linux you can specify it using the ``-L-L``
|
||||
option, and link with D:YAML using the ``-L-l`` option. On Windows, the import
|
||||
directory is used as the library directory. To link with the library on Windows,
|
||||
just add the path to it relative to the current directory.
|
||||
``-I`` option of DMD. The library directory should be where you put the compiled
|
||||
D:YAML library. On Unix/Linux you can specify it using the ``-L-L`` option, and
|
||||
link with D:YAML using the ``-L-l`` option. On Windows, the import directory is
|
||||
used as the library directory. To link with the library on Windows, just add the
|
||||
path to it relative to the current directory.
|
||||
|
||||
For example, if you extracted D:YAML to ``/home/xxx/dyaml`` and compiled it in
|
||||
that directory, your project is in ``/home/xxx/dyaml-project``, and you are
|
||||
currently in that directory, you can compile the project with the following
|
||||
command on Unix/Linux::
|
||||
For example, if you extracted and compiled D:YAML in ``/home/xxx/dyaml``, your
|
||||
project is in ``/home/xxx/dyaml-project``, and you are currently in that
|
||||
directory, you can compile the project with the following command on Unix/Linux::
|
||||
|
||||
dmd -I../dyaml -L-L../dyaml -L-ldyaml main.d
|
||||
|
||||
|
|
|
@ -8,14 +8,14 @@ which this article is based on,
|
|||
`Chapter 2 of the YAML specification <http://yaml.org/spec/1.1/#id857168>`_
|
||||
or the `Wikipedia page <http://en.wikipedia.org/wiki/YAML>`_.
|
||||
|
||||
YAML is a data serialization format designed to be as human readable as
|
||||
possible. YAML is a recursive acronym for "YAML Ain't Markup Language".
|
||||
YAML is a data serialization format designed for human readability. YAML is a
|
||||
recursive acronym for "YAML Ain't Markup Language".
|
||||
|
||||
YAML is similar to JSON, and in fact, JSON is a subset of YAML 1.2; but YAML has
|
||||
some more advanced features and is easier to read. However, YAML is also more
|
||||
some more advanced features and is easier to read. However, it is also more
|
||||
difficult to parse (and probably somewhat slower). Data is stored in mappings
|
||||
(associative arrays), sequences (lists) and scalars (single values). Data
|
||||
structure hierarchy either depends on indentation (block context, similar to
|
||||
structure hierarchy depends either on indentation (block context, similar to
|
||||
Python code), or nesting of brackets and braces (flow context, similar to JSON).
|
||||
YAML comments begin with ``#`` and continue until the end of line.
|
||||
|
||||
|
@ -25,8 +25,7 @@ Documents
|
|||
---------
|
||||
|
||||
A YAML stream consists of one or more documents starting with ``---`` and
|
||||
optionally ending with ``...`` . If there is only one document, ``---`` can be
|
||||
left out.
|
||||
optionally ending with ``...`` . ``---`` can be left out for the first document.
|
||||
|
||||
Single document with no explicit start or end:
|
||||
|
||||
|
|
|
@ -40,14 +40,14 @@ import dyaml.tagdirectives;
|
|||
* Write to a file:
|
||||
* --------------------
|
||||
* auto node = Node([1, 2, 3, 4, 5]);
|
||||
* Dumper("file.txt").dump(node);
|
||||
* Dumper("file.yaml").dump(node);
|
||||
* --------------------
|
||||
*
|
||||
* Write multiple YAML documents to a file:
|
||||
* --------------------
|
||||
* auto node1 = Node([1, 2, 3, 4, 5]);
|
||||
* auto node2 = Node("This document contains only one string");
|
||||
* Dumper("file.txt").dump(node1, node2);
|
||||
* Dumper("file.yaml").dump(node1, node2);
|
||||
* --------------------
|
||||
*
|
||||
* Write to memory:
|
||||
|
@ -65,8 +65,8 @@ import dyaml.tagdirectives;
|
|||
* auto resolver = new Resolver();
|
||||
*
|
||||
* //Add representer functions / resolver expressions here...
|
||||
* --------------------
|
||||
* auto dumper = Dumper("file.txt");
|
||||
*
|
||||
* auto dumper = Dumper("file.yaml");
|
||||
* dumper.representer = representer;
|
||||
* dumper.resolver = resolver;
|
||||
* dumper.dump(node);
|
||||
|
@ -259,7 +259,7 @@ struct Dumper
|
|||
*
|
||||
* Example:
|
||||
* --------------------
|
||||
* Dumper dumper = Dumper("file.txt");
|
||||
* Dumper dumper = Dumper("file.yaml");
|
||||
* //This will emit tags starting with "tag:long.org,2011"
|
||||
* //with a "!short!" prefix instead.
|
||||
* dumper.tags("short", "tag:long.org,2011:");
|
||||
|
|
352
dyaml/loader.d
352
dyaml/loader.d
|
@ -28,43 +28,36 @@ import dyaml.token;
|
|||
|
||||
|
||||
/**
|
||||
* Load single YAML document from a file.
|
||||
* Loads YAML documents from files or streams.
|
||||
*
|
||||
* If there is no or more than one YAML document in the file, this will throw.
|
||||
* Use $(LREF loadAll) for such files.
|
||||
*
|
||||
* Params: filename = Name of the file to _load from.
|
||||
*
|
||||
* Returns: Root node of the document.
|
||||
*
|
||||
* Throws: YAMLException if there wasn't exactly one document in the file,
|
||||
* the file could not be opened or on a YAML parsing error.
|
||||
*/
|
||||
Node load(in string filename)
|
||||
{
|
||||
auto loader = Loader(filename);
|
||||
return loader.loadSingleDocument();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load single YAML document from a stream.
|
||||
*
|
||||
* You can use this to e.g _load YAML from memory.
|
||||
*
|
||||
* If there is no or more than one YAML document in the stream, this will throw.
|
||||
* Use $(LREF loadAll) for such files.
|
||||
*
|
||||
* Params: input = Stream to read from. Must be readable.
|
||||
* name = Name of the stream, used in error messages.
|
||||
*
|
||||
* Returns: Root node of the document.
|
||||
*
|
||||
* Throws: YAMLException if there wasn't exactly one document in the stream,
|
||||
* the stream could not be read from or on a YAML parsing error.
|
||||
* User specified Constructor and/or Resolver can be used to support new
|
||||
* tags / data types.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* Loading YAML from memory:
|
||||
* Load single YAML document from a file:
|
||||
* --------------------
|
||||
* auto rootNode = Loader("file.yaml").load();
|
||||
* ...
|
||||
* --------------------
|
||||
*
|
||||
* Load all YAML documents from a file:
|
||||
* --------------------
|
||||
* auto nodes = Loader("file.yaml").loadAll();
|
||||
* ...
|
||||
* --------------------
|
||||
*
|
||||
* Iterate over YAML documents in a file, lazily loading them:
|
||||
* --------------------
|
||||
* auto loader = Loader("file.yaml");
|
||||
*
|
||||
* foreach(ref node; loader)
|
||||
* {
|
||||
* ...
|
||||
* }
|
||||
* --------------------
|
||||
*
|
||||
* Load YAML from memory:
|
||||
* --------------------
|
||||
* import std.stream;
|
||||
* import std.stdio;
|
||||
|
@ -73,74 +66,27 @@ Node load(in string filename)
|
|||
* "green: '#00ff00'\n"
|
||||
* "blue: '#0000ff'";
|
||||
*
|
||||
* auto colors = yaml.load(new MemoryStream(cast(char[])yaml_input));
|
||||
* auto colors = Loader(new MemoryStream(cast(char[])yaml_input)).load();
|
||||
*
|
||||
* foreach(string color, string value; colors)
|
||||
* {
|
||||
* writeln(color, " is ", value, " in HTML/CSS");
|
||||
* }
|
||||
* --------------------
|
||||
*
|
||||
* Use a custom constructor/resolver to support custom data types and/or implicit tags:
|
||||
* --------------------
|
||||
* auto constructor = new Constructor();
|
||||
* auto resolver = new Resolver();
|
||||
*
|
||||
* //Add constructor functions / resolver expressions here...
|
||||
*
|
||||
* auto loader = Loader("file.yaml");
|
||||
* loader.constructor = constructor;
|
||||
* loader.resolver = resolver;
|
||||
* auto rootNode = loader.load(node);
|
||||
* --------------------
|
||||
*/
|
||||
Node load(Stream input, in string name = "<unknown>")
|
||||
{
|
||||
auto loader = Loader(input, name, new Constructor, new Resolver);
|
||||
return loader.loadSingleDocument();
|
||||
}
|
||||
unittest
|
||||
{
|
||||
import std.stream;
|
||||
import std.stdio;
|
||||
|
||||
string yaml_input = "red: '#ff0000'\n"
|
||||
"green: '#00ff00'\n"
|
||||
"blue: '#0000ff'";
|
||||
|
||||
auto colors = load(new MemoryStream(cast(char[])yaml_input));
|
||||
|
||||
foreach(string color, string value; colors)
|
||||
{
|
||||
writeln(color, " is ", value, " in HTML/CSS");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all YAML documents from a file.
|
||||
*
|
||||
* Params: filename = Name of the file to load from.
|
||||
*
|
||||
* Returns: Array of root nodes of documents in the stream.
|
||||
* If the stream is empty, empty array will be returned.
|
||||
*
|
||||
* Throws: YAMLException if the file could not be opened or on a YAML parsing error.
|
||||
*/
|
||||
Node[] loadAll(in string filename)
|
||||
{
|
||||
auto loader = Loader(filename);
|
||||
Node[] result;
|
||||
foreach(ref node; loader){result ~= node;}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all YAML documents from a stream.
|
||||
*
|
||||
* Params: input = Stream to read from. Must be readable.
|
||||
* name = Name of the stream, used in error messages.
|
||||
*
|
||||
* Returns: Array of root nodes of documents in the file.
|
||||
* If the file is empty, empty array will be returned.
|
||||
*
|
||||
* Throws: YAMLException if the stream could not be read from or on a YAML parsing error.
|
||||
*/
|
||||
Node[] loadAll(Stream input, in string name = "<unknown>")
|
||||
{
|
||||
auto loader = Loader(input, name, new Constructor, new Resolver);
|
||||
Node[] result;
|
||||
foreach(ref node; loader){result ~= node;}
|
||||
return result;
|
||||
}
|
||||
|
||||
///Loads YAML documents from files or streams.
|
||||
struct Loader
|
||||
{
|
||||
private:
|
||||
|
@ -154,12 +100,8 @@ struct Loader
|
|||
Resolver resolver_;
|
||||
///Constructs YAML data types.
|
||||
Constructor constructor_;
|
||||
///Composes YAML nodes.
|
||||
Composer composer_;
|
||||
///Name of the input file or stream, used in error messages.
|
||||
string name_;
|
||||
///Input file stream, if the stream is created by Loader itself.
|
||||
File file_ = null;
|
||||
string name_ = "<unknown>";
|
||||
|
||||
public:
|
||||
/**
|
||||
|
@ -171,60 +113,33 @@ struct Loader
|
|||
*/
|
||||
this(in string filename)
|
||||
{
|
||||
try{file_ = new File(filename);}
|
||||
try
|
||||
{
|
||||
name = filename;
|
||||
this(new File(filename));
|
||||
}
|
||||
catch(StreamException e)
|
||||
{
|
||||
throw new YAMLException("Unable to load YAML file " ~ filename ~ " : " ~ e.msg);
|
||||
}
|
||||
this(file_, filename, new Constructor, new Resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a Loader to load YAML from a file, with provided _constructor and _resolver.
|
||||
* Construct a Loader to load YAML from a stream.
|
||||
*
|
||||
* Constructor and _resolver can be used to implement custom data types in YAML.
|
||||
*
|
||||
* Params: filename = Name of the file to load from.
|
||||
* constructor = Constructor to use.
|
||||
* resolver = Resolver to use.
|
||||
*
|
||||
* Throws: YAMLException if the file could not be opened or read from.
|
||||
*/
|
||||
this(in string filename, Constructor constructor, Resolver resolver)
|
||||
{
|
||||
try{file_ = new File(filename);}
|
||||
catch(StreamException e)
|
||||
{
|
||||
throw new YAMLException("Unable to load YAML file " ~ filename ~ " : " ~ e.msg);
|
||||
}
|
||||
|
||||
this(file_, filename, constructor, resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a Loader to load YAML from a stream with provided _constructor and _resolver.
|
||||
*
|
||||
* Stream can be used to load YAML from memory and other sources.
|
||||
* Constructor and _resolver can be used to implement custom data types in YAML.
|
||||
*
|
||||
* Params: input = Stream to read from. Must be readable.
|
||||
* name = Name of the stream. Used in error messages.
|
||||
* constructor = Constructor to use.
|
||||
* resolver = Resolver to use.
|
||||
* Params: stream = Stream to read from. Must be readable.
|
||||
*
|
||||
* Throws: YAMLException if the stream could not be read from.
|
||||
*/
|
||||
this(Stream input, in string name, Constructor constructor, Resolver resolver)
|
||||
this(Stream stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
reader_ = new Reader(input);
|
||||
reader_ = new Reader(stream);
|
||||
scanner_ = new Scanner(reader_);
|
||||
parser_ = new Parser(scanner_);
|
||||
resolver_ = resolver;
|
||||
constructor_ = constructor;
|
||||
composer_ = new Composer(parser_, resolver_, constructor_);
|
||||
name_ = name;
|
||||
resolver_ = new Resolver;
|
||||
constructor_ = new Constructor;
|
||||
Anchor.addReference();
|
||||
TagDirectives.addReference();
|
||||
}
|
||||
|
@ -235,58 +150,6 @@ struct Loader
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load single YAML document.
|
||||
*
|
||||
* If no or more than one YAML document is found, this will throw a YAMLException.
|
||||
*
|
||||
* Returns: Root node of the document.
|
||||
*
|
||||
* Throws: YAMLException if there wasn't exactly one document
|
||||
* or on a YAML parsing error.
|
||||
*/
|
||||
Node loadSingleDocument()
|
||||
{
|
||||
try
|
||||
{
|
||||
enforce(composer_.checkNode(), new YAMLException("No YAML document to load"));
|
||||
return composer_.getSingleNode();
|
||||
}
|
||||
catch(YAMLException e)
|
||||
{
|
||||
e.name = name_;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Foreach over YAML documents.
|
||||
*
|
||||
* Parses documents lazily, as they are needed.
|
||||
*
|
||||
* Throws: YAMLException on a parsing error.
|
||||
*/
|
||||
int opApply(int delegate(ref Node) dg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int result = 0;
|
||||
while(composer_.checkNode())
|
||||
{
|
||||
auto node = composer_.getNode();
|
||||
result = dg(node);
|
||||
if(result){break;}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch(YAMLException e)
|
||||
{
|
||||
e.name = name_;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
///Destroy the Loader.
|
||||
~this()
|
||||
{
|
||||
|
@ -295,9 +158,97 @@ struct Loader
|
|||
clear(reader_);
|
||||
clear(scanner_);
|
||||
clear(parser_);
|
||||
clear(composer_);
|
||||
//Can't clear constructor, resolver: they might be supplied by the user.
|
||||
if(file_ !is null){file_.close();}
|
||||
}
|
||||
|
||||
///Set stream name. Used in debugging messages.
|
||||
@property void name(string name)
|
||||
{
|
||||
name_ = name;
|
||||
}
|
||||
|
||||
///Set Resolver to use.
|
||||
@property void resolver(Resolver resolver)
|
||||
{
|
||||
resolver_ = resolver;
|
||||
}
|
||||
|
||||
///Set Constructor to use.
|
||||
@property void constructor(Constructor constructor)
|
||||
{
|
||||
constructor_ = constructor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load single YAML document.
|
||||
*
|
||||
* If no or more than one YAML document is found, this will throw a YAMLException.
|
||||
*
|
||||
* Returns: Root node of the document.
|
||||
*
|
||||
* Throws: YAMLException if there wasn't exactly one document
|
||||
* or on a YAML parsing error.
|
||||
*/
|
||||
Node load()
|
||||
{
|
||||
try
|
||||
{
|
||||
auto composer = new Composer(parser_, resolver_, constructor_);
|
||||
enforce(composer.checkNode(), new YAMLException("No YAML document to load"));
|
||||
return composer.getSingleNode();
|
||||
}
|
||||
catch(YAMLException e)
|
||||
{
|
||||
e.name = name_;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all YAML documents.
|
||||
*
|
||||
* Returns: Array of root nodes of all documents in the file/stream.
|
||||
*
|
||||
* Throws: YAMLException if there wasn't exactly one document
|
||||
* or on a YAML parsing error.
|
||||
*/
|
||||
Node[] loadAll()
|
||||
{
|
||||
Node[] nodes;
|
||||
foreach(ref node; this)
|
||||
{
|
||||
nodes ~= node;
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Foreach over YAML documents.
|
||||
*
|
||||
* Parses documents lazily, as they are needed.
|
||||
*
|
||||
* Throws: YAMLException on a parsing error.
|
||||
*/
|
||||
int opApply(int delegate(ref Node) dg)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto composer = new Composer(parser_, resolver_, constructor_);
|
||||
|
||||
int result = 0;
|
||||
while(composer.checkNode())
|
||||
{
|
||||
auto node = composer.getNode();
|
||||
result = dg(node);
|
||||
if(result){break;}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch(YAMLException e)
|
||||
{
|
||||
e.name = name_;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
package:
|
||||
|
@ -333,3 +284,20 @@ struct Loader
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
import std.stream;
|
||||
import std.stdio;
|
||||
|
||||
string yaml_input = "red: '#ff0000'\n"
|
||||
"green: '#00ff00'\n"
|
||||
"blue: '#0000ff'";
|
||||
|
||||
auto colors = Loader(new MemoryStream(cast(char[])yaml_input)).load();
|
||||
|
||||
foreach(string color, string value; colors)
|
||||
{
|
||||
writeln(color, " is ", value, " in HTML/CSS");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,9 +88,10 @@ void main()
|
|||
constructor.addConstructor("!color", &constructColorScalar);
|
||||
constructor.addConstructor("!color-mapping", &constructColorMapping);
|
||||
|
||||
auto loader = new Loader("input.yaml", constructor, new Resolver);
|
||||
auto loader = Loader("input.yaml");
|
||||
loader.constructor = constructor;
|
||||
|
||||
auto root = loader.loadSingleDocument();
|
||||
auto root = loader.load();
|
||||
|
||||
if(root["scalar-red"].get!Color == red &&
|
||||
root["mapping-red"].get!Color == red &&
|
||||
|
|
|
@ -3,7 +3,7 @@ import yaml;
|
|||
|
||||
void main()
|
||||
{
|
||||
yaml.Node root = yaml.load("input.yaml");
|
||||
yaml.Node root = Loader("input.yaml").load();
|
||||
foreach(string word; root["Hello World"])
|
||||
{
|
||||
writeln(word);
|
||||
|
|
|
@ -92,9 +92,11 @@ void main()
|
|||
resolver.addImplicitResolver("!color", std.regex.regex("[0-9a-fA-F]{6}"),
|
||||
"0123456789abcdefABCDEF");
|
||||
|
||||
auto loader = new Loader("input.yaml", constructor, resolver);
|
||||
auto loader = Loader("input.yaml");
|
||||
loader.constructor = constructor;
|
||||
loader.resolver = resolver;
|
||||
|
||||
auto root = loader.loadSingleDocument();
|
||||
auto root = loader.load();
|
||||
|
||||
if(root["scalar-red"].get!Color == red &&
|
||||
root["mapping-red"].get!Color == red &&
|
||||
|
|
|
@ -42,8 +42,8 @@ void testParser(bool verbose, string dataFilename, string canonicalFilename)
|
|||
*/
|
||||
void testLoader(bool verbose, string dataFilename, string canonicalFilename)
|
||||
{
|
||||
auto data = loadAll(dataFilename);
|
||||
auto canonical = loadAll(canonicalFilename);
|
||||
auto data = Loader(dataFilename).loadAll;
|
||||
auto canonical = Loader(canonicalFilename).loadAll;
|
||||
|
||||
assert(data.length == canonical.length, "Unequal node count");
|
||||
foreach(n; 0 .. data.length)
|
||||
|
|
|
@ -398,8 +398,9 @@ void testConstructor(bool verbose, string dataFilename, string codeDummy)
|
|||
constructor.addConstructor("!tag1", &constructClass);
|
||||
constructor.addConstructor("!tag2", &constructStruct);
|
||||
|
||||
auto resolver = new Resolver;
|
||||
auto loader = Loader(dataFilename, constructor, resolver);
|
||||
auto loader = Loader(dataFilename);
|
||||
loader.constructor = constructor;
|
||||
loader.resolver = new Resolver;
|
||||
|
||||
Node[] exp = expected[base];
|
||||
|
||||
|
|
|
@ -92,8 +92,12 @@ void testEmitterOnData(bool verbose, string dataFilename, string canonicalFilena
|
|||
writeln("ORIGINAL:\n", readText(dataFilename));
|
||||
writeln("OUTPUT:\n", cast(string)emitStream.data);
|
||||
}
|
||||
auto loadStream = new MemoryStream(emitStream.data);
|
||||
auto newEvents = Loader(loadStream, "DUMMY", new Constructor, new Resolver).parse();
|
||||
|
||||
auto loader2 = Loader(new MemoryStream(emitStream.data));
|
||||
loader2.name = "TEST";
|
||||
loader2.constructor = new Constructor;
|
||||
loader2.resolver = new Resolver;
|
||||
auto newEvents = loader2.parse();
|
||||
assert(compareEvents(events, newEvents));
|
||||
}
|
||||
|
||||
|
@ -121,8 +125,11 @@ void testEmitterOnCanonical(bool verbose, string canonicalFilename)
|
|||
writeln("OUTPUT (canonical=", canonical, "):\n",
|
||||
cast(string)emitStream.data);
|
||||
}
|
||||
auto loadStream = new MemoryStream(emitStream.data);
|
||||
auto newEvents = Loader(loadStream, "DUMMY", new Constructor, new Resolver).parse();
|
||||
auto loader2 = Loader(new MemoryStream(emitStream.data));
|
||||
loader2.name = "TEST";
|
||||
loader2.constructor = new Constructor;
|
||||
loader2.resolver = new Resolver;
|
||||
auto newEvents = loader2.parse();
|
||||
assert(compareEvents(events, newEvents));
|
||||
}
|
||||
}
|
||||
|
@ -179,8 +186,11 @@ void testEmitterStyles(bool verbose, string dataFilename, string canonicalFilena
|
|||
to!string(style), ")");
|
||||
writeln(emitStream.data);
|
||||
}
|
||||
auto loadStream = new MemoryStream(emitStream.data);
|
||||
auto newEvents = Loader(loadStream, "DUMMY", new Constructor, new Resolver).parse();
|
||||
auto loader2 = Loader(new MemoryStream(emitStream.data));
|
||||
loader2.name = "TEST";
|
||||
loader2.constructor = new Constructor;
|
||||
loader2.resolver = new Resolver;
|
||||
auto newEvents = loader2.parse();
|
||||
assert(compareEvents(events, newEvents));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ void testLoaderError(bool verbose, string errorFilename)
|
|||
scope(exit){file.close();}
|
||||
|
||||
Node[] nodes;
|
||||
try{nodes = loadAll(file, errorFilename);}
|
||||
try{nodes = Loader(file).loadAll();}
|
||||
catch(YAMLException e)
|
||||
{
|
||||
if(verbose){writeln(typeid(e).toString(), "\n", e);}
|
||||
|
@ -48,7 +48,7 @@ void testLoaderErrorString(bool verbose, string errorFilename)
|
|||
|
||||
try
|
||||
{
|
||||
auto nodes = loadAll(new MemoryStream(buffer), errorFilename);
|
||||
auto nodes = Loader(new MemoryStream(buffer)).loadAll();
|
||||
}
|
||||
catch(YAMLException e)
|
||||
{
|
||||
|
@ -66,7 +66,7 @@ void testLoaderErrorString(bool verbose, string errorFilename)
|
|||
*/
|
||||
void testLoaderErrorFilename(bool verbose, string errorFilename)
|
||||
{
|
||||
try{auto nodes = loadAll(errorFilename);}
|
||||
try{auto nodes = Loader(errorFilename).loadAll();}
|
||||
catch(YAMLException e)
|
||||
{
|
||||
if(verbose){writeln(typeid(e).toString(), "\n", e);}
|
||||
|
@ -83,7 +83,7 @@ void testLoaderErrorFilename(bool verbose, string errorFilename)
|
|||
*/
|
||||
void testLoaderErrorSingle(bool verbose, string errorFilename)
|
||||
{
|
||||
try{auto nodes = load(errorFilename);}
|
||||
try{auto nodes = Loader(errorFilename).load();}
|
||||
catch(YAMLException e)
|
||||
{
|
||||
if(verbose){writeln(typeid(e).toString(), "\n", e);}
|
||||
|
|
|
@ -57,13 +57,13 @@ void testUnicodeInput(bool verbose, string unicodeFilename)
|
|||
string data = readText(unicodeFilename);
|
||||
string expected = data.split().join(" ");
|
||||
|
||||
Node output = load(new MemoryStream(to!(char[])(data)), unicodeFilename);
|
||||
Node output = Loader(new MemoryStream(to!(char[])(data))).load();
|
||||
assert(output.get!string == expected);
|
||||
|
||||
foreach(stream; [new MemoryStream(cast(byte[])(bom16() ~ to!(wchar[])(data))),
|
||||
new MemoryStream(cast(byte[])(bom32() ~ to!(dchar[])(data)))])
|
||||
{
|
||||
output = load(stream, unicodeFilename);
|
||||
output = Loader(stream).load();
|
||||
assert(output.get!string == expected);
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ void testUnicodeInputErrors(bool verbose, string unicodeFilename)
|
|||
new MemoryStream(cast(byte[])(bom16(true) ~ to!(wchar[])(data))),
|
||||
new MemoryStream(cast(byte[])(bom32(true) ~ to!(dchar[])(data)))])
|
||||
{
|
||||
try{load(stream, unicodeFilename);}
|
||||
try{Loader(stream).load();}
|
||||
catch(YAMLException e)
|
||||
{
|
||||
if(verbose){writeln(typeid(e).toString(), "\n", e);}
|
||||
|
|
|
@ -61,9 +61,10 @@ void testRepresenterTypes(bool verbose, string codeFilename)
|
|||
constructor.addConstructor("!tag1", &constructClass);
|
||||
constructor.addConstructor("!tag2", &constructStruct);
|
||||
|
||||
auto resolver = new Resolver;
|
||||
auto loader = Loader(loadStream, "DUMMY", constructor, resolver);
|
||||
foreach(node; loader){readNodes ~= node;}
|
||||
auto loader = Loader(loadStream);
|
||||
loader.name = "TEST";
|
||||
loader.constructor = constructor;
|
||||
readNodes = loader.loadAll();
|
||||
|
||||
assert(expectedNodes.length == readNodes.length);
|
||||
foreach(n; 0 .. expectedNodes.length)
|
||||
|
|
|
@ -17,27 +17,31 @@ import dyaml.testcommon;
|
|||
* Implicit tag resolution unittest.
|
||||
*
|
||||
* Params: verbose = Print verbose output?
|
||||
* dataFilename = TODO
|
||||
* detectFilename = TODO
|
||||
* dataFilename = File with unittest data.
|
||||
* detectFilename = Dummy filename used to specify which data filenames to use.
|
||||
*/
|
||||
void testImplicitResolver(bool verbose, string dataFilename, string detectFilename)
|
||||
{
|
||||
string correctTag;
|
||||
Node node;
|
||||
|
||||
scope(exit)
|
||||
scope(failure)
|
||||
{
|
||||
if(verbose)
|
||||
if(true)
|
||||
{
|
||||
writeln("Correct tag: ", correctTag);
|
||||
writeln("Node: ", node.debugString);
|
||||
assert(node.isSequence);
|
||||
assert(node.tag.get == correctTag);
|
||||
}
|
||||
}
|
||||
|
||||
correctTag = readText(dataFilename).strip();
|
||||
node = yaml.load(dataFilename);
|
||||
correctTag = readText(detectFilename).strip();
|
||||
node = Loader(dataFilename).load();
|
||||
assert(node.isSequence);
|
||||
foreach(ref Node scalar; node)
|
||||
{
|
||||
assert(scalar.isScalar);
|
||||
assert(scalar.tag.get == correctTag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue