diff --git a/autoddoc.py b/autoddoc.py index 5f050b9..26f685b 100755 --- a/autoddoc.py +++ b/autoddoc.py @@ -294,7 +294,7 @@ default_css =\ " border: 0.6em solid #cccccc;\n" " background-color: #f6f6f6;\n" " font-size: 0.875em;\n" - " line-height: 1.4em;\n" + " line-height: 1.3em;\n" "}\n" "\n" "div#content li{padding-bottom: .7ex;}\n" diff --git a/doc/doctrees/environment.pickle b/doc/doctrees/environment.pickle index 62f0768..eb0504b 100644 Binary files a/doc/doctrees/environment.pickle and b/doc/doctrees/environment.pickle differ diff --git a/doc/doctrees/tutorials/custom_types.doctree b/doc/doctrees/tutorials/custom_types.doctree index 7518da8..a523422 100644 Binary files a/doc/doctrees/tutorials/custom_types.doctree and b/doc/doctrees/tutorials/custom_types.doctree differ diff --git a/doc/doctrees/tutorials/getting_started.doctree b/doc/doctrees/tutorials/getting_started.doctree index c535cb0..0f9bf45 100644 Binary files a/doc/doctrees/tutorials/getting_started.doctree and b/doc/doctrees/tutorials/getting_started.doctree differ diff --git a/doc/html/_sources/tutorials/custom_types.txt b/doc/html/_sources/tutorials/custom_types.txt index 65853ee..8f6e565 100644 --- a/doc/html/_sources/tutorials/custom_types.txt +++ b/doc/html/_sources/tutorials/custom_types.txt @@ -3,7 +3,7 @@ Custom YAML data types ====================== Sometimes you need to serialize complex data types such as classes. To do this -you could use plain nodes such as mappings with class data members. YAML also +you could use plain nodes such as mappings with classes' fields. YAML also supports custom types with identifiers called *tags*. That is the topic of this tutorial. @@ -23,11 +23,13 @@ functions to process each supported tag. These are supplied by the user using the *addConstructorXXX()* methods, where *XXX* is *Scalar*, *Sequence* or *Mapping*. *Constructor* is then passed to *Loader*, which parses YAML input. -Struct types have no specific requirements for YAML support. Class types should -define the *opEquals()* operator - this is used in equality comparisons of -nodes. Default class *opEquals()* compares references, which means two identical -objects might be considered unequal. (Default struct *opEquals()* compares -byte-by-byte, sometimes you might want to override that as well.) +Structs and classes must implement the *opCmp()* operator for YAML support. This +is used for duplicate detection in mappings, sorting and equality comparisons of +nodes. The signature of the operator that must be implemented is +``const int opCmp(ref const MyStruct s)`` for structs where *MyStruct* is the +struct type, and ``int opCmp(Object o)`` for classes. Note that the class +*opCmp()* should not alter the compared values - it is not const for compatibility +reasons. We will implement support for an RGB color type. It is implemented as the following struct: @@ -39,6 +41,14 @@ following struct: ubyte red; ubyte green; ubyte blue; + + const int opCmp(ref const Color c) + { + if(red != c.red) {return red - c.red;} + if(green != c.green){return green - c.green;} + if(blue != c.blue) {return blue - c.blue;} + return 0; + } } First, we need a function to construct our data type. The function will take a diff --git a/doc/html/_sources/tutorials/getting_started.txt b/doc/html/_sources/tutorials/getting_started.txt index 55c451b..5b7ed81 100644 --- a/doc/html/_sources/tutorials/getting_started.txt +++ b/doc/html/_sources/tutorials/getting_started.txt @@ -43,7 +43,7 @@ Do this by typing the following command into the console:: dmd cdc.d -Now you can use CDC to compile D:YAML. +Now compile D:YAML with CDC. To do this on Unix/Linux, use the following command:: ./cdc @@ -101,8 +101,8 @@ into the file: 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. +First, we import the *yaml* module. This is the only D:YAML module you need to +import - it automatically imports all needed modules. Next we load the file using the *Loader.load()* method. *Loader* is a struct used for parsing YAML documents. The *load()* method loads the file as @@ -146,18 +146,18 @@ formatted differently. Comments are not preserved, either. 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 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. +To compile your project, DMD needs to know which directories contain the +imported 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 the +compiled 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 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:: +directory, compile the project with the following command on Unix/Linux:: dmd -I../dyaml -L-L../dyaml -L-ldyaml main.d diff --git a/doc/html/api/css/style.css b/doc/html/api/css/style.css index 4dcf993..65dbada 100644 --- a/doc/html/api/css/style.css +++ b/doc/html/api/css/style.css @@ -190,7 +190,7 @@ div#content border: 0.6em solid #cccccc; background-color: #f6f6f6; font-size: 0.875em; - line-height: 1.4em; + line-height: 1.3em; } div#content li{padding-bottom: .7ex;} diff --git a/doc/html/api/dyaml.constructor.html b/doc/html/api/dyaml.constructor.html index f05fd59..e393fbc 100644 --- a/doc/html/api/dyaml.constructor.html +++ b/doc/html/api/dyaml.constructor.html @@ -72,6 +72,8 @@

Each YAML scalar, sequence or mapping has a tag specifying its data type. Constructor uses user-specifyable functions to create a node of desired data type from a scalar, sequence or mapping. +
+
Each of these functions is associated with a tag, and can process either @@ -82,7 +84,7 @@ If a tag is detected with no known constructor function, it is considered an error.

-
this(in const(bool) defaultConstructors = true); +
this(const(bool) defaultConstructors = true);

Construct a Constructor.

@@ -107,12 +109,24 @@ Any exception thrown by this function will be caught by D:YAML and its message will be added to a YAMLException that will also tell the user which type failed to construct, and position in the file. +
+
The value returned by this function will be stored in the resulting node.
Only one constructor function can be set for one tag. +
+ +
+ + Structs and classes must implement the opCmp() operator for D:YAML + support. The signature of the operator that must be implemented + is const int opCmp(ref const MyStruct s) for structs where + MyStruct is the struct type, and int opCmp(Object o) for + classes. Note that the class opCmp() should not alter the compared + values - it is not const for compatibility reasons.

Parameters:
@@ -128,6 +142,16 @@ struct MyStruct { int x, y, z; + + //Any D:YAML type must have a custom opCmp operator. +//This is used for ordering in mappings. +constint opCmp(refconst MyStruct s) + { + if(x != s.x){return x - s.x;} + if(y != s.y){return y - s.y;} + if(z != s.z){return z - s.z;} + return 0; + } } MyStruct constructMyStructScalar(ref Node node) @@ -166,6 +190,16 @@ struct MyStruct { int x, y, z; + + //Any D:YAML type must have a custom opCmp operator. +//This is used for ordering in mappings. +constint opCmp(refconst MyStruct s) + { + if(x != s.x){return x - s.x;} + if(y != s.y){return y - s.y;} + if(z != s.z){return z - s.z;} + return 0; + } } MyStruct constructMyStructSequence(ref Node node) @@ -202,6 +236,16 @@ struct MyStruct { int x, y, z; + + //Any D:YAML type must have a custom opCmp operator. +//This is used for ordering in mappings. +constint opCmp(refconst MyStruct s) + { + if(x != s.x){return x - s.x;} + if(y != s.y){return y - s.y;} + if(z != s.z){return z - s.z;} + return 0; + } } MyStruct constructMyStructMapping(ref Node node) @@ -227,42 +271,42 @@
YAMLNull constructNull(ref Node node);
-

Construct a null node.

+

Construct a null node.

YAMLMerge constructMerge(ref Node node);
-

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.

bool constructBool(ref Node node);
-

Construct a boolean node.

+

Construct a boolean node.

long constructLong(ref Node node);
-

Construct an integer (long) node.

+

Construct an integer (long) node.

real constructReal(ref Node node);
-

Construct a floating point (real) node.

+

Construct a floating point (real) node.

ubyte[] constructBinary(ref Node node);
-

Construct a binary (base64) node.

+

Construct a binary (base64) node.

SysTime constructTimestamp(ref Node node);
-

Construct a timestamp (SysTime) node.

+

Construct a timestamp (SysTime) node.

string constructString(ref Node node);
-

Construct a string node.

+

Construct a string node.

Pair[] getPairs(string type, Node[] nodes); @@ -272,27 +316,27 @@
Pair[] constructOrderedMap(ref Node node);
-

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.

Pair[] constructPairs(ref Node node);
-

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[] constructSet(ref Node node);
-

Construct a set node.

+

Construct a set node.

Node[] constructSequence(ref Node node);
-

Construct a sequence (array) node.

+

Construct a sequence (array) node.

Pair[] constructMap(ref Node 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.

@@ -301,7 +345,7 @@ diff --git a/doc/html/api/dyaml.dumper.html b/doc/html/api/dyaml.dumper.html index 5e9cf90..ad23d75 100644 --- a/doc/html/api/dyaml.dumper.html +++ b/doc/html/api/dyaml.dumper.html @@ -224,7 +224,7 @@ diff --git a/doc/html/api/dyaml.encoding.html b/doc/html/api/dyaml.encoding.html index 5aafb6a..8f7eea3 100644 --- a/doc/html/api/dyaml.encoding.html +++ b/doc/html/api/dyaml.encoding.html @@ -65,7 +65,7 @@ diff --git a/doc/html/api/dyaml.exception.html b/doc/html/api/dyaml.exception.html index 3ed7bea..5ccdcbe 100644 --- a/doc/html/api/dyaml.exception.html +++ b/doc/html/api/dyaml.exception.html @@ -57,7 +57,7 @@

Position in a YAML stream, used for error messages.

-
this(in const(uint) line, in const(uint) column); +
this(const(uint) line, const(uint) column);

Construct a Mark with specified line and column in the file.

@@ -75,7 +75,7 @@ diff --git a/doc/html/api/dyaml.linebreak.html b/doc/html/api/dyaml.linebreak.html index b348d29..d8b5ab6 100644 --- a/doc/html/api/dyaml.linebreak.html +++ b/doc/html/api/dyaml.linebreak.html @@ -65,7 +65,7 @@ diff --git a/doc/html/api/dyaml.loader.html b/doc/html/api/dyaml.loader.html index b36dd25..6804e3f 100644 --- a/doc/html/api/dyaml.loader.html +++ b/doc/html/api/dyaml.loader.html @@ -190,7 +190,7 @@ diff --git a/doc/html/api/dyaml.node.html b/doc/html/api/dyaml.node.html index a991ebd..aca8074 100644 --- a/doc/html/api/dyaml.node.html +++ b/doc/html/api/dyaml.node.html @@ -400,7 +400,7 @@
int opApply(T)(int delegate(ref T) dg);
-

Iterate over a sequence, getting each element as T. +

Foreach over a sequence, getting each element as T.

If T is Node, simply iterate over the nodes in the sequence. Otherwise, convert each node to T during iteration. @@ -412,7 +412,7 @@

int opApply(K, V)(int delegate(ref K, ref V) dg);
-

Iterate over a mapping, getting each key/value as K/V. +

Foreach over a mapping, getting each key/value as K/V.

If the K and/or V is Node, simply iterate over the nodes in the mapping. Otherwise, convert each key/value to T during iteration. @@ -512,6 +512,11 @@ Throws:

NodeException if the node is not a collection, index is out of range or if a non-integral index is used on a sequence node.
+
+
const const int opCmp(ref const Node node); +
+

Compare with another node.

+
@@ -521,7 +526,7 @@ diff --git a/doc/html/api/dyaml.representer.html b/doc/html/api/dyaml.representer.html index 9acb9da..b0ed32e 100644 --- a/doc/html/api/dyaml.representer.html +++ b/doc/html/api/dyaml.representer.html @@ -52,7 +52,7 @@
class Representer;
-

Represents YAML nodes of various data types as scalar, sequence and mapping nodes ready for output. +

Represents YAML nodes as scalar, sequence and mapping nodes ready for output.

This class is used to add support for dumping of custom data types.
@@ -73,26 +73,38 @@

@property void defaultScalarStyle(ScalarStyle style);
-

Set default style for scalars. Invalid means the style is chosen automatically.

+

Set default style for scalars. If style is ScalarStyle.Invalid, the style is chosen automatically.

@property void defaultCollectionStyle(CollectionStyle style);
-

Set default style for collections. Invalid means the style is chosen automatically.

+

Set default style for collections. If style is CollectionStyle.Invalid, the style is chosen automatically.

void addRepresenter(T)(Node function(ref Node, Representer) representer);

Add a function to represent nodes with a specific data type.

-

The representer function takes references to a Node storing the data - type and to the Representer. It returns the represented node and may - throw a RepresenterException. See the example for more information. +

The representer function takes references to a Node storing the data + type and to the Representer. It returns the represented node and may + throw a RepresenterException. See the example for more information. +
+
Only one function may be specified for one data type. Default data types already have representer functions unless disabled in the - Representer constructor. + Representer constructor. +
+ +
+ + Structs and classes must implement the opCmp() operator for D:YAML + support. The signature of the operator that must be implemented + is const int opCmp(ref const MyStruct s) for structs where + MyStruct is the struct type, and int opCmp(Object o) for + classes. Note that the class opCmp() should not alter the compared + values - it is not const for compatibility reasons.

Parameters:
tag
@@ -106,6 +118,16 @@ struct MyStruct { int x, y, z; + + //Any D:YAML type must have a custom opCmp operator. +//This is used for ordering in mappings. +constint opCmp(refconst MyStruct s) + { + if(x != s.x){return x - s.x;} + if(y != s.y){return y - s.y;} + if(z != s.z){return z - s.z;} + return 0; + } } Node representMyStruct(ref Node node, Representer representer) @@ -144,12 +166,16 @@ this.z = z; } - ///We need custom opEquals for node equality, as default opEquals compares references. -overridebool opEquals(Object rhs) + //Any D:YAML type must have a custom opCmp operator. +//This is used for ordering in mappings. +overrideint opCmp(Object o) { - if(typeid(rhs) != typeid(MyClass)){returnfalse;} - auto t = cast(MyClass)rhs; - return x == t.x && y == t.y && z == t.z; + MyClass s = cast(MyClass)o; + if(s isnull){return -1;} + if(x != s.x){return x - s.x;} + if(y != s.y){return y - s.y;} + if(z != s.z){return z - s.z;} + return 0; } ///Useful for Node.as!string . @@ -204,6 +230,16 @@
 struct MyStruct
  {
      int x, y, z;
+
+     //Any D:YAML type must have a custom opCmp operator.
+     //This is used for ordering in mappings.
+     const int opCmp(ref const MyStruct s)
+     {
+         if(x != s.x){return x - s.x;}
+         if(y != s.y){return y - s.y;}
+         if(z != s.z){return z - s.z;}
+         return 0;
+     }
  }
 
  Node representMyStruct(ref Node node, Representer representer)
@@ -211,7 +247,6 @@
      auto value = node.as!MyStruct;
      auto scalar = format(value.x, ":", value.y, ":", value.z);
      return representer.representScalar("!mystruct.tag", scalar);
-
  }
 

@@ -225,7 +260,7 @@

Parameters:
representer
- + @@ -235,13 +270,23 @@ Returns:
The represented node.
-Throws:
RepresenterException if a child could not be represented. +Throws:
RepresenterException if a child could not be represented.

Example:

 struct MyStruct
  {
      int x, y, z;
+
+     //Any D:YAML type must have a custom opCmp operator.
+     //This is used for ordering in mappings.
+     const int opCmp(ref const MyStruct s)
+     {
+         if(x != s.x){return x - s.x;}
+         if(y != s.y){return y - s.y;}
+         if(z != s.z){return z - s.z;}
+         return 0;
+     }
  }
 
  Node representMyStruct(ref Node node, Representer representer)
@@ -274,13 +319,23 @@
 Returns:
The represented node.
-Throws:
RepresenterException if a child could not be represented. +Throws:
RepresenterException if a child could not be represented.

Example:

 struct MyStruct
  {
      int x, y, z;
+
+     //Any D:YAML type must have a custom opCmp operator.
+     //This is used for ordering in mappings.
+     const int opCmp(ref const MyStruct s)
+     {
+         if(x != s.x){return x - s.x;}
+         if(y != s.y){return y - s.y;}
+         if(z != s.z){return z - s.z;}
+         return 0;
+     }
  }
 
  Node representMyStruct(ref Node node, Representer representer)
@@ -348,7 +403,7 @@
 
 
 
 
diff --git a/doc/html/api/dyaml.resolver.html b/doc/html/api/dyaml.resolver.html
index 3cc3f2d..6d47a31 100644
--- a/doc/html/api/dyaml.resolver.html
+++ b/doc/html/api/dyaml.resolver.html
@@ -133,7 +133,7 @@
 
 
 
 
diff --git a/doc/html/api/dyaml.style.html b/doc/html/api/dyaml.style.html
index e81aa39..98539a9 100644
--- a/doc/html/api/dyaml.style.html
+++ b/doc/html/api/dyaml.style.html
@@ -96,7 +96,7 @@
 
 
 
 
diff --git a/doc/html/api/index.html b/doc/html/api/index.html
index 0480490..12317a5 100644
--- a/doc/html/api/index.html
+++ b/doc/html/api/index.html
@@ -59,7 +59,7 @@ import the yaml module, which will import all needed modules.
 
 
 
 
diff --git a/doc/html/articles/spec_differences.html b/doc/html/articles/spec_differences.html
index 10219d9..d2daa87 100644
--- a/doc/html/articles/spec_differences.html
+++ b/doc/html/articles/spec_differences.html
@@ -138,7 +138,7 @@ struct appears in Phobos.

diff --git a/doc/html/index.html b/doc/html/index.html index 394af19..7f94386 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -104,7 +104,7 @@
diff --git a/doc/html/search.html b/doc/html/search.html index 6dcbd2b..0832c77 100644 --- a/doc/html/search.html +++ b/doc/html/search.html @@ -87,7 +87,7 @@ diff --git a/doc/html/searchindex.js b/doc/html/searchindex.js index 8b3e9d3..bed5587 100644 --- a/doc/html/searchindex.js +++ b/doc/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({objects:{},terms:{represent:[2,3],all:[1,3,4],code:[0,1,3,4],representmap:3,scalar:[0,1,2,3,4],follow:[3,4],decid:3,depend:[0,3],show:[0,3],readabl:0,specif:[0,1,3,2],representsequ:3,program:4,digit:[3,4],sourc:[3,4],everi:3,string:[0,3,4],powervr:0,"void":[3,4],phobo:1,ident:3,failur:3,veri:1,implicitli:[0,3],tri:1,gender:0,list:[0,1,2],iter:4,"try":[3,4],item:0,refer:[0,3],slower:0,past:4,fold:0,second:[0,4],design:0,pass:3,download:4,rrr:3,even:[1,4],index:4,what:3,appear:1,compar:3,cast:3,preserv:[0,4],section:3,current:4,version:4,"new":3,method:[3,4],dyaml:[1,4],never:1,here:4,ggg:3,ldyaml:4,path:4,modifi:4,implicit:[0,3],valu:[0,1,3,4],convert:[0,3,4],male:0,loos:1,datetim:0,omap:0,chang:[0,1,3],commonli:4,modul:4,unix:4,subnod:4,instal:4,articl:[0,2],regex:3,from:3,would:[3,4],doubl:0,two:[0,1,3],next:[3,4],few:1,call:[3,4],msg:3,type:[0,1,2,3,4],tell:4,more:[0,4],phone:0,known:[1,2,4],hold:3,must:[3,4],addrepresent:3,word:4,alia:[0,1],work:1,itself:3,can:[0,1,3,4],learn:4,purpos:3,root:[3,4],overrid:3,want:3,stream:[0,1],give:4,process:3,templat:3,topic:3,tag:[0,1,3,2],tab:1,serial:[0,3],addconstructorxxx:3,cours:3,multipl:[0,1],newlin:0,quot:[0,1],getting_start:4,how:[3,4],foreach:[3,4],answer:[0,4],instead:0,simpl:[0,3,4],css:3,map:[0,1,2,3,4],mar:4,clone:0,variant:1,befor:3,ff0000:3,uint:3,mai:1,end:[0,1],data:[0,1,2,3,4],welcom:[2,4],ani:[0,3,4],stdio:4,explicit:[0,3],correspond:3,ambigu:1,caus:1,inform:[0,4],green:[0,3],allow:[0,1],first:[0,2,3,4],order:[1,4],over:4,move:4,orang:3,becaus:1,yamlexcept:[3,4],hierarchi:0,still:[0,1],paramet:3,write:4,style:[0,4],precondit:3,yaml:[0,1,2,3,4],window:4,complex:[0,3],main:[3,4],might:[0,1,3,4],easier:0,them:[0,1,3,4],good:3,"return":[3,4],thei:[0,1,3,4],python:0,auto:3,now:[1,3,4],introduct:[0,4],name:0,separ:0,each:[0,3],found:[3,4],went:3,complet:3,mean:[1,3],compil:[3,4],unequ:[1,3],idea:4,"static":3,expect:[3,4],our:[3,4],happen:3,extract:4,out:[0,3],space:0,content:[1,3,4],rel:4,"0123456789abcdefabcdef":3,ref:3,red:[0,3],difficulti:1,advanc:0,"0123456789abcdef":3,base:[0,3],dictionari:0,put:[3,4],org:[1,3],"byte":[1,3],thrown:[3,4],pyyaml:[0,1],indent:[0,1],could:[3,4],keep:4,length:3,yamlcoloninflowcontext:1,confus:1,assign:0,radeon:0,oper:[0,3,4],rang:3,onc:[0,1],arrai:[0,1,3,4],sometim:3,restrict:1,unlik:0,alreadi:[3,4],done:3,construct:[0,3],stabl:4,miss:3,addconstructormap:3,size:0,differ:[1,2,3,4],dump:[3,4],script:4,associ:[0,1,4],addimplicitresolv:3,system:[0,4],messag:3,gpu:0,white:3,conveni:0,"final":[3,4],store:[0,3],adher:1,consol:4,option:[0,4],especi:0,ishexdigit:3,specifi:[0,3,4],pars:[0,3,4],somewhat:0,exactli:4,than:1,serv:4,remov:0,structur:[0,1],charact:[0,1,3],project:[2,4],str:[0,3],were:3,argument:3,packag:[3,4],have:[0,3,4],tabl:0,need:[0,1,3,4],"null":0,lib:4,alias:[0,1,2],constructcolormap:3,note:[3,4],also:[0,3,4],read:[0,3,4],take:3,which:[0,1,3],brace:0,channel:3,uppercas:3,blue:[0,3],begin:[0,1],normal:4,previou:3,most:[0,3,4],regular:3,pair:[0,3],"class":[1,3],opequ:3,don:[1,3,4],later:3,flow:[0,1],doe:[0,1,4],bracket:0,cortex:0,fact:0,ldc:4,cdc:4,alphanumer:1,syntax:[0,2],identifi:[0,3],find:[3,4],onli:[3,4],explicitli:[0,3],just:[3,4],explain:4,should:[1,3,4],meant:4,std:[3,4],get:[2,3,4],express:3,cannot:[1,4],report:3,requir:3,whether:3,common:0,contain:[0,1,3,4],where:[0,3,4],wiki:1,set:[0,2,3,4],seq:0,"float":[0,3],see:[0,1,3],result:3,fail:1,close:1,athlon:0,representscalar:3,wikipedia:0,written:4,between:[0,1,2],"import":4,accord:1,kei:[0,1,4],both:3,last:0,howev:[0,3],equal:3,comment:[0,4],etc:3,tutori:[2,3,4],context:[0,1],load:[1,3,4],color:[0,3],hyphen:0,loader:[3,4],colon:0,suppli:3,respect:3,addconstructorscalar:3,rgb:3,empti:0,sinc:3,json:0,basic:4,xxx:[3,4],anywher:0,assert:3,togeth:3,input:[3,4],"catch":3,multi:0,therefor:[1,3],look:4,plain:[0,1,3],ffff00:3,defin:[0,3],ain:0,error:4,anchor:[0,1,2],readi:3,non:[0,3],archiv:4,tediou:3,ascii:[1,3],dumper:[3,4],disabl:3,clearli:1,make:3,same:[0,1,3,4],check:[3,4],member:3,binari:0,instanc:[1,3,4],timestamp:0,android:0,document:[0,1,2,3,4],difficult:0,http:1,nest:0,user:[0,3],implement:[1,3],markup:0,well:3,person:0,exampl:[3,4],command:4,thi:[0,1,3,4],everyth:3,left:0,explan:4,systim:0,newest:4,execut:4,less:1,paragraph:0,gdc:4,human:0,yet:4,languag:[0,4],struct:[1,3,4],libdyaml:4,interptet:1,except:[3,4],constructcolorscalar:3,add:[3,4],other:[1,4],els:0,match:3,build:4,real:0,format:[0,3,4],handl:[1,3],know:3,world:4,recurs:[0,1],tolow:3,like:3,success:3,arbitrari:3,resolv:[0,2,3],integ:[0,3,4],arthur:0,api:[2,3,4],singl:[0,1],output:[3,4],unnecessari:1,right:1,linux:[0,4],some:[0,1,3,4],guarante:3,ubyt:[0,3],librari:4,representcolor:3,lead:1,though:4,per:3,unord:1,condit:3,either:[0,3,4],object:3,run:4,acronym:0,usag:4,immut:3,rrggbb:3,unicod:1,chapter:0,comparison:3,about:[1,4],rare:1,usedefaultrepresent:3,page:0,constructor:[2,3],fals:3,produc:4,block:0,subset:0,within:1,encod:1,automat:4,bbb:3,bsd:0,mark:[0,1],your:[2,3,4],wai:[1,3],support:[0,1,3],question:0,"long":0,custom:[0,2,3],writeln:[3,4],start:[0,2,3,4],"function":3,form:3,continu:0,link:4,line:0,conclus:4,"throw":[3,4],dmd:4,consist:0,possibl:[0,1,3,4],"default":[0,3],displai:4,until:0,directori:[3,4],problem:1,similar:[0,4],featur:[0,1],creat:[0,3,4],"int":[0,4],cover:4,dure:3,parser:[0,4],doesn:4,repres:[0,3,4],"char":3,exist:4,file:[1,3,4],yamlnul:0,isdigit:3,dent:0,simplest:4,probabl:0,hex:3,when:[3,4],detail:[0,1],invalid:3,valid:1,bool:0,futur:[0,1],test:3,you:[0,3,4],node:[0,3,4],resolut:[0,3],sequenc:[0,2,3,4],consid:[1,3],debian:0,reduc:0,longer:3,home:4,rule:1,hello:4,ignor:1,far:3,escap:0,cpu:0},objtypes:{},titles:["YAML syntax","Differences between D:YAML and the YAML specification","Welcome to D:YAML documentation!","Custom YAML data types","Getting started"],objnames:{},filenames:["tutorials/yaml_syntax","articles/spec_differences","index","tutorials/custom_types","tutorials/getting_started"]}) \ No newline at end of file +Search.setIndex({objects:{},terms:{represent:[2,3],all:[1,3,4],code:[0,1,3,4],representmap:3,scalar:[0,1,2,3,4],follow:[3,4],decid:3,"const":3,show:[0,3],readabl:0,specif:[0,1,2],representsequ:3,program:4,digit:[3,4],sourc:[3,4],everi:3,string:[0,3,4],fals:3,"void":[3,4],phobo:1,failur:3,veri:1,implicitli:[0,3],tri:1,gender:0,list:[0,1,2],iter:4,"try":[3,4],item:0,refer:[0,3],slower:0,past:4,fold:0,second:[0,4],design:0,pass:3,download:4,rrr:3,even:[1,4],index:4,what:3,appear:1,compar:3,cast:3,section:3,current:4,version:4,"new":3,method:[3,4],dyaml:[1,4],never:1,here:4,ggg:3,ldyaml:4,depend:[0,3],modifi:4,sinc:3,valu:[0,1,3,4],convert:[0,3,4],male:0,loos:1,datetim:0,omap:0,chang:[0,1,3],commonli:4,modul:4,unix:4,subnod:4,instal:4,articl:[0,2],regex:3,from:3,would:[3,4],doubl:0,two:[0,1],next:[3,4],few:1,call:[3,4],msg:3,type:[0,1,2,3,4],tell:4,more:[0,4],sort:3,main:[3,4],python:0,phone:0,known:[1,2,4],hold:3,must:3,addrepresent:3,word:4,alia:[0,1],work:1,itself:3,can:[0,1,3,4],learn:4,purpos:3,root:[3,4],overrid:3,want:3,stream:[0,1],process:3,templat:3,topic:3,tag:[0,1,3,2],tab:1,serial:[0,3],addconstructorxxx:3,cours:3,multipl:[0,1],newlin:0,quot:[0,1],getting_start:4,how:[3,4],foreach:[3,4],answer:[0,4],instead:0,simpl:[0,3,4],css:3,map:[0,1,2,3,4],mar:4,clone:0,variant:1,befor:3,ff0000:3,uint:3,mai:1,end:[0,1],data:[0,1,2,3,4],welcom:[2,4],ani:[0,3,4],stdio:4,explicit:[0,3],correspond:3,ambigu:1,caus:1,inform:[0,4],green:[0,3],allow:[0,1],first:[0,2,3,4],order:[1,4],over:4,move:4,orang:3,becaus:1,yamlexcept:[3,4],hierarchi:0,still:[0,1],paramet:3,write:4,style:[0,4],precondit:3,yaml:[0,1,2,3,4],window:4,complex:[0,3],therefor:[1,3],might:[0,1,3,4],alter:3,them:[0,1,3,4],good:3,"return":[3,4],thei:[0,1,3,4],handl:[1,3],auto:3,now:[1,3,4],introduct:[0,4],name:0,separ:0,each:[0,3],found:[3,4],went:3,complet:3,mean:1,compil:[3,4],unequ:1,idea:4,"static":3,expect:[3,4],our:[3,4],happen:3,extract:4,out:[0,3],space:0,content:[1,3,4],rel:4,"0123456789abcdefabcdef":3,ref:3,red:[0,3],difficulti:1,advanc:0,"0123456789abcdef":3,mystruct:3,reason:3,base:[0,3],dictionari:0,put:3,org:[1,3],"byte":1,thrown:[3,4],pyyaml:[0,1],indent:[0,1],could:[3,4],keep:4,length:3,yamlcoloninflowcontext:1,confus:1,assign:0,radeon:0,oper:[0,3,4],rang:3,onc:[0,1],arrai:[0,1,3,4],sometim:3,restrict:1,unlik:0,alreadi:[3,4],done:3,construct:[0,3],stabl:4,miss:3,addconstructormap:3,size:0,differ:[1,2,3,4],seq:0,script:4,associ:[0,1,4],addimplicitresolv:3,system:[0,4],messag:3,gpu:0,white:3,conveni:0,"final":[3,4],store:[0,3],adher:1,consol:4,option:[0,4],especi:0,ishexdigit:3,specifi:[0,3,4],pars:[0,3,4],somewhat:0,exactli:4,than:1,serv:4,remov:0,structur:[0,1],charact:[0,1,3],project:[2,4],str:[0,3],were:3,argument:3,packag:[3,4],have:[0,3,4],tabl:0,need:[0,1,3,4],"null":0,lib:4,alias:[0,1,2],constructcolormap:3,note:[3,4],also:[0,3,4],read:[0,3,4],take:3,which:[0,1,3,4],brace:0,channel:3,uppercas:3,compat:3,begin:[0,1],normal:4,previou:3,most:[0,3,4],detect:3,pair:[0,3],"class":[1,3],don:[1,3,4],later:3,flow:[0,1],doe:[0,1,4],bracket:0,cortex:0,fact:0,ldc:4,cdc:4,alphanumer:1,syntax:[0,2],identifi:[0,3],find:[3,4],onli:[3,4],explicitli:[0,3],just:[3,4],explain:4,should:[1,3,4],conclus:4,meant:4,std:[3,4],get:[2,3,4],express:3,cannot:[1,4],report:3,whether:3,common:0,contain:[0,1,3,4],where:[0,3],wiki:1,set:[0,2,3,4],dump:[3,4],"float":[0,3],see:[0,1,3],result:3,fail:1,close:1,athlon:0,representscalar:3,wikipedia:0,written:4,between:[0,1,2],powervr:0,"import":4,signatur:3,accord:1,kei:[0,1,4],both:3,last:0,howev:[0,3],equal:3,comment:[0,4],etc:3,tutori:[2,3,4],context:[0,1],load:[1,3,4],point:4,color:[0,3],hyphen:0,loader:[3,4],colon:0,path:4,respect:3,addconstructorscalar:3,rgb:3,empti:0,implicit:[0,3],json:0,basic:4,xxx:[3,4],anywher:0,assert:3,togeth:3,input:[3,4],"catch":3,multi:0,ident:3,look:4,plain:[0,1,3],ffff00:3,easier:0,defin:0,ain:0,error:4,anchor:[0,1,2],readi:3,non:[0,3],archiv:4,tediou:3,ascii:[1,3],dumper:[3,4],disabl:3,clearli:1,make:3,same:[0,1,3,4],check:[3,4],binari:0,instanc:[1,3,4],timestamp:0,android:0,document:[0,1,2,3,4],difficult:0,http:1,nest:0,blue:[0,3],user:[0,3],implement:[1,3],markup:0,well:3,person:0,exampl:[3,4],command:4,thi:[0,1,3,4],everyth:3,left:0,explan:4,systim:0,newest:4,execut:4,less:1,paragraph:0,gdc:4,human:0,yet:4,languag:[0,4],struct:[1,3,4],libdyaml:4,interptet:1,except:[3,4],constructcolorscalar:3,add:[3,4],other:[1,4],els:0,match:3,build:4,real:0,format:[0,3,4],preserv:[0,4],know:[3,4],world:4,recurs:[0,1],linux:[0,4],tolow:3,like:3,success:3,arbitrari:3,resolv:[0,2,3],integ:[0,3,4],arthur:0,api:[2,3,4],singl:[0,1],output:[3,4],unnecessari:1,right:1,suppli:3,some:[0,1,3,4],guarante:3,ubyt:[0,3],librari:4,representcolor:3,lead:1,though:4,per:3,unord:1,condit:3,duplic:3,either:[0,3,4],object:3,run:4,acronym:0,usag:4,immut:3,rrggbb:3,unicod:1,chapter:0,comparison:3,about:[1,4],rare:1,usedefaultrepresent:3,page:0,regular:3,constructor:[2,3],produc:4,block:0,subset:0,within:1,encod:1,automat:4,bbb:3,bsd:0,mark:[0,1],your:[2,3,4],wai:[1,3],support:[0,1,3],question:0,"long":0,custom:[0,2,3],writeln:[3,4],start:[0,2,3,4],"function":3,form:3,continu:0,link:4,line:0,opcmp:3,"throw":[3,4],dmd:4,consist:0,possibl:[0,1,3,4],"default":[0,3],displai:4,until:0,directori:[3,4],problem:1,similar:[0,4],featur:[0,1],creat:[0,3,4],"int":[0,3,4],cover:4,dure:3,parser:[0,4],doesn:4,repres:[0,3,4],"char":3,exist:4,file:[1,3,4],yamlnul:0,isdigit:3,dent:0,simplest:4,probabl:0,hex:3,when:[3,4],detail:[0,1],invalid:3,field:3,valid:1,bool:0,futur:[0,1],test:3,you:[0,3,4],node:[0,3,4],resolut:[0,3],sequenc:[0,2,3,4],consid:1,debian:0,reduc:0,longer:3,home:4,rule:1,hello:4,ignor:1,far:3,escap:0,cpu:0},objtypes:{},titles:["YAML syntax","Differences between D:YAML and the YAML specification","Welcome to D:YAML documentation!","Custom YAML data types","Getting started"],objnames:{},filenames:["tutorials/yaml_syntax","articles/spec_differences","index","tutorials/custom_types","tutorials/getting_started"]}) \ No newline at end of file diff --git a/doc/html/tutorials/custom_types.html b/doc/html/tutorials/custom_types.html index fe96d63..4625af6 100644 --- a/doc/html/tutorials/custom_types.html +++ b/doc/html/tutorials/custom_types.html @@ -48,7 +48,7 @@

Custom YAML data types

Sometimes you need to serialize complex data types such as classes. To do this -you could use plain nodes such as mappings with class data members. YAML also +you could use plain nodes such as mappings with classes’ fields. YAML also supports custom types with identifiers called tags. That is the topic of this tutorial.

Each YAML node has a tag specifying its type. For instance: strings use the tag @@ -62,11 +62,13 @@ each node to hold data type corresponding to its tag. Constructor store functions to process each supported tag. These are supplied by the user using the addConstructorXXX() methods, where XXX is Scalar, Sequence or Mapping. Constructor is then passed to Loader, which parses YAML input.

-

Struct types have no specific requirements for YAML support. Class types should -define the opEquals() operator - this is used in equality comparisons of -nodes. Default class opEquals() compares references, which means two identical -objects might be considered unequal. (Default struct opEquals() compares -byte-by-byte, sometimes you might want to override that as well.)

+

Structs and classes must implement the opCmp() operator for YAML support. This +is used for duplicate detection in mappings, sorting and equality comparisons of +nodes. The signature of the operator that must be implemented is +const int opCmp(ref const MyStruct s) for structs where MyStruct is the +struct type, and int opCmp(Object o) for classes. Note that the class +opCmp() should not alter the compared values - it is not const for compatibility +reasons.

We will implement support for an RGB color type. It is implemented as the following struct:

struct Color
@@ -74,6 +76,14 @@ following struct:

ubyte red; ubyte green; ubyte blue; + + const int opCmp(ref const Color c) + { + if(red != c.red) {return red - c.red;} + if(green != c.green){return green - c.green;} + if(blue != c.blue) {return blue - c.blue;} + return 0; + } }
@@ -364,7 +374,7 @@ directory of the D:YAML package.

diff --git a/doc/html/tutorials/getting_started.html b/doc/html/tutorials/getting_started.html index 43d7eb3..047d8c5 100644 --- a/doc/html/tutorials/getting_started.html +++ b/doc/html/tutorials/getting_started.html @@ -78,7 +78,7 @@ script for compilation. To compile D:YAML, you first need to build CDC. Do this by typing the following command into the console:

dmd cdc.d
-

Now you can use CDC to compile D:YAML. +

Now compile D:YAML with CDC. To do this on Unix/Linux, use the following command:

./cdc
@@ -125,8 +125,8 @@ into the file:

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.

+

First, we import the yaml module. This is the only D:YAML module you need to +import - it automatically imports all needed modules.

Next we load the file using the Loader.load() method. Loader is a struct used for parsing YAML documents. The load() method loads the file as one YAML document, or throws YAMLException, D:YAML exception type, if the @@ -160,17 +160,17 @@ formatted differently. Comments are not preserved, either.

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 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.

+

To compile your project, DMD needs to know which directories contain the +imported 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 the +compiled 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 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:

+directory, compile the project with the following command on Unix/Linux:

dmd -I../dyaml -L-L../dyaml -L-ldyaml main.d

And the following on Windows:

@@ -237,7 +237,7 @@ example in the example/getting_st
diff --git a/doc/html/tutorials/yaml_syntax.html b/doc/html/tutorials/yaml_syntax.html index 255c713..4b607c3 100644 --- a/doc/html/tutorials/yaml_syntax.html +++ b/doc/html/tutorials/yaml_syntax.html @@ -330,7 +330,7 @@ Some of these might change in the future (especially !!map and !!set).

diff --git a/docsrc/tutorials/custom_types.rst b/docsrc/tutorials/custom_types.rst index 65853ee..8f6e565 100644 --- a/docsrc/tutorials/custom_types.rst +++ b/docsrc/tutorials/custom_types.rst @@ -3,7 +3,7 @@ Custom YAML data types ====================== Sometimes you need to serialize complex data types such as classes. To do this -you could use plain nodes such as mappings with class data members. YAML also +you could use plain nodes such as mappings with classes' fields. YAML also supports custom types with identifiers called *tags*. That is the topic of this tutorial. @@ -23,11 +23,13 @@ functions to process each supported tag. These are supplied by the user using the *addConstructorXXX()* methods, where *XXX* is *Scalar*, *Sequence* or *Mapping*. *Constructor* is then passed to *Loader*, which parses YAML input. -Struct types have no specific requirements for YAML support. Class types should -define the *opEquals()* operator - this is used in equality comparisons of -nodes. Default class *opEquals()* compares references, which means two identical -objects might be considered unequal. (Default struct *opEquals()* compares -byte-by-byte, sometimes you might want to override that as well.) +Structs and classes must implement the *opCmp()* operator for YAML support. This +is used for duplicate detection in mappings, sorting and equality comparisons of +nodes. The signature of the operator that must be implemented is +``const int opCmp(ref const MyStruct s)`` for structs where *MyStruct* is the +struct type, and ``int opCmp(Object o)`` for classes. Note that the class +*opCmp()* should not alter the compared values - it is not const for compatibility +reasons. We will implement support for an RGB color type. It is implemented as the following struct: @@ -39,6 +41,14 @@ following struct: ubyte red; ubyte green; ubyte blue; + + const int opCmp(ref const Color c) + { + if(red != c.red) {return red - c.red;} + if(green != c.green){return green - c.green;} + if(blue != c.blue) {return blue - c.blue;} + return 0; + } } First, we need a function to construct our data type. The function will take a diff --git a/docsrc/tutorials/getting_started.rst b/docsrc/tutorials/getting_started.rst index 55c451b..5b7ed81 100644 --- a/docsrc/tutorials/getting_started.rst +++ b/docsrc/tutorials/getting_started.rst @@ -43,7 +43,7 @@ Do this by typing the following command into the console:: dmd cdc.d -Now you can use CDC to compile D:YAML. +Now compile D:YAML with CDC. To do this on Unix/Linux, use the following command:: ./cdc @@ -101,8 +101,8 @@ into the file: 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. +First, we import the *yaml* module. This is the only D:YAML module you need to +import - it automatically imports all needed modules. Next we load the file using the *Loader.load()* method. *Loader* is a struct used for parsing YAML documents. The *load()* method loads the file as @@ -146,18 +146,18 @@ formatted differently. Comments are not preserved, either. 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 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. +To compile your project, DMD needs to know which directories contain the +imported 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 the +compiled 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 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:: +directory, compile the project with the following command on Unix/Linux:: dmd -I../dyaml -L-L../dyaml -L-ldyaml main.d diff --git a/dyaml/constructor.d b/dyaml/constructor.d index d766924..517a681 100644 --- a/dyaml/constructor.d +++ b/dyaml/constructor.d @@ -15,6 +15,7 @@ module dyaml.constructor; import std.array; import std.algorithm; import std.base64; +import std.container; import std.conv; import std.datetime; import std.exception; @@ -59,6 +60,7 @@ private alias ConstructorException Error; * Constructor uses user-specifyable functions to create a node of desired * data type from a scalar, sequence or mapping. * + * * Each of these functions is associated with a tag, and can process either * a scalar, a sequence, or a mapping. The constructor passes each value to * the function with corresponding tag, which then returns the resulting value @@ -130,10 +132,19 @@ final class Constructor * its message will be added to a YAMLException that will also tell the * user which type failed to construct, and position in the file. * + * * The value returned by this function will be stored in the resulting node. * * Only one constructor function can be set for one tag. * + * + * Structs and classes must implement the $(D opCmp()) operator for D:YAML + * support. The signature of the operator that must be implemented + * is $(D const int opCmp(ref const MyStruct s)) for structs where + * $(I MyStruct) is the struct type, and $(D int opCmp(Object o)) for + * classes. Note that the class $(D opCmp()) should not alter the compared + * values - it is not const for compatibility reasons. + * * Params: tag = Tag for the function to handle. * ctor = Constructor function. * @@ -147,6 +158,16 @@ final class Constructor * struct MyStruct * { * int x, y, z; + * + * //Any D:YAML type must have a custom opCmp operator. + * //This is used for ordering in mappings. + * const int opCmp(ref const MyStruct s) + * { + * if(x != s.x){return x - s.x;} + * if(y != s.y){return y - s.y;} + * if(z != s.z){return z - s.z;} + * return 0; + * } * } * * MyStruct constructMyStructScalar(ref Node node) @@ -190,6 +211,16 @@ final class Constructor * struct MyStruct * { * int x, y, z; + * + * //Any D:YAML type must have a custom opCmp operator. + * //This is used for ordering in mappings. + * const int opCmp(ref const MyStruct s) + * { + * if(x != s.x){return x - s.x;} + * if(y != s.y){return y - s.y;} + * if(z != s.z){return z - s.z;} + * return 0; + * } * } * * MyStruct constructMyStructSequence(ref Node node) @@ -231,6 +262,16 @@ final class Constructor * struct MyStruct * { * int x, y, z; + * + * //Any D:YAML type must have a custom opCmp operator. + * //This is used for ordering in mappings. + * const int opCmp(ref const MyStruct s) + * { + * if(x != s.x){return x - s.x;} + * if(y != s.y){return y - s.y;} + * if(z != s.z){return z - s.z;} + * return 0; + * } * } * * MyStruct constructMyStructMapping(ref Node node) @@ -330,19 +371,19 @@ final class Constructor } -///Construct a null node. +///Construct a _null _node. YAMLNull constructNull(ref Node node) { return YAMLNull(); } -///Construct a merge node - a node that merges another node into a mapping. +///Construct a merge _node - a _node that merges another _node into a mapping. YAMLMerge constructMerge(ref Node node) { return YAMLMerge(); } -///Construct a boolean node. +///Construct a boolean _node. bool constructBool(ref Node node) { static yes = ["yes", "true", "on"]; @@ -353,7 +394,7 @@ bool constructBool(ref Node node) throw new Exception("Unable to parse boolean value: " ~ value); } -///Construct an integer (long) node. +///Construct an integer (long) _node. long constructLong(ref Node node) { string value = node.as!string().replace("_", ""); @@ -421,7 +462,7 @@ unittest assert(685230 == getLong(sexagesimal)); } -///Construct a floating point (real) node. +///Construct a floating point (real) _node. real constructReal(ref Node node) { string value = node.as!string().replace("_", "").toLower(); @@ -491,7 +532,7 @@ unittest assert(to!string(getReal(NaN)) == "nan"); } -///Construct a binary (base64) node. +///Construct a binary (base64) _node. ubyte[] constructBinary(ref Node node) { string value = node.as!string; @@ -519,7 +560,7 @@ unittest assert(value == test); } -///Construct a timestamp (SysTime) node. +///Construct a timestamp (SysTime) _node. SysTime constructTimestamp(ref Node node) { string value = node.as!string; @@ -618,7 +659,7 @@ unittest assert(timestamp(ymd) == "20021214T000000Z"); } -///Construct a string node. +///Construct a string _node. string constructString(ref Node node) { return node.as!string; @@ -641,22 +682,22 @@ Node.Pair[] getPairs(string type, Node[] nodes) return pairs; } -///Construct an ordered map (ordered sequence of key:value pairs without duplicates) node. +///Construct an ordered map (ordered sequence of key:value pairs without duplicates) _node. Node.Pair[] constructOrderedMap(ref Node node) { auto pairs = getPairs("ordered map", node.as!(Node[])); - //TODO: the map here should be replaced with something with deterministic - //memory allocation if possible. - //Detect duplicates. - bool[Node] map; + //Detect duplicates. + //TODO this should be replaced by something with deterministic memory allocation. + auto keys = redBlackTree!Node(); + scope(exit){clear(keys);} foreach(ref pair; pairs) { - enforce((pair.key in map) is null, - new Exception("Duplicate entry in an ordered map")); - map[pair.key] = true; + enforce(!(pair.key in keys), + new Exception("Duplicate entry in an ordered map: " + ~ pair.key.debugString())); + keys.insert(pair.key); } - clear(map); return pairs; } unittest @@ -701,13 +742,13 @@ unittest assert(!hasDuplicates(alternateTypes(64))); } -///Construct a pairs (ordered sequence of key: value pairs allowing duplicates) node. +///Construct a pairs (ordered sequence of key: value pairs allowing duplicates) _node. Node.Pair[] constructPairs(ref Node node) { return getPairs("pairs", node.as!(Node[])); } -///Construct a set node. +///Construct a set _node. Node[] constructSet(ref Node node) { auto pairs = node.as!(Node.Pair[]); @@ -771,26 +812,26 @@ unittest (constructSet(Node(noDuplicatesLong.dup)))); } -///Construct a sequence (array) node. +///Construct a sequence (array) _node. Node[] constructSequence(ref Node node) { return node.as!(Node[]); } -///Construct an unordered map (unordered set of key: value _pairs without duplicates) node. +///Construct an unordered map (unordered set of key:value _pairs without duplicates) _node. Node.Pair[] constructMap(ref Node node) { auto pairs = node.as!(Node.Pair[]); - //TODO: the map here should be replaced with something with deterministic - //memory allocation if possible. - //Detect duplicates. - bool[Node] map; - scope(exit){clear(map);} + //Detect duplicates. + //TODO this should be replaced by something with deterministic memory allocation. + auto keys = redBlackTree!Node(); + scope(exit){clear(keys);} foreach(ref pair; pairs) { - enforce((pair.key in map) is null, - new Exception("Duplicate entry in a map")); - map[pair.key] = true; + enforce(!(pair.key in keys), + new Exception("Duplicate entry in a map: " + ~ pair.key.debugString())); + keys.insert(pair.key); } return pairs; } @@ -805,6 +846,14 @@ import dyaml.loader; struct MyStruct { int x, y, z; + + const int opCmp(ref const MyStruct s) + { + if(x != s.x){return x - s.x;} + if(y != s.y){return y - s.y;} + if(z != s.z){return z - s.z;} + return 0; + } } MyStruct constructMyStructScalar(ref Node node) diff --git a/dyaml/node.d b/dyaml/node.d index 649a497..fdea2a8 100644 --- a/dyaml/node.d +++ b/dyaml/node.d @@ -69,16 +69,16 @@ package struct YAMLMerge{} package abstract class YAMLObject { public: - ///Get type of the stored value. + //Get type of the stored value. @property TypeInfo type() const {assert(false);} protected: - ///Test for equality with another YAMLObject. - bool equals(const YAMLObject rhs) const {assert(false);} + //Compare with another YAMLObject. + int cmp(const YAMLObject rhs) const {assert(false);}; } //Stores a user defined YAML data type. -package class YAMLContainer(T) if (!Node.Value.allowed!T): YAMLObject +package class YAMLContainer(T) if (!Node.allowed!T): YAMLObject { private: //Stored value. @@ -102,11 +102,16 @@ package class YAMLContainer(T) if (!Node.Value.allowed!T): YAMLObject } protected: - //Test for equality with another YAMLObject. - override bool equals(const YAMLObject rhs) const + //Compare with another YAMLObject. + override int cmp(const YAMLObject rhs) const { - if(rhs.type !is typeid(T)){return false;} - return cast(T)value_ == (cast(const YAMLContainer)rhs).value_; + const typeCmp = type.opCmp(rhs.type); + if(typeCmp != 0){return typeCmp;} + + //Const-casting here as Object opCmp is not const. + T* v1 = cast(T*)&value_; + T* v2 = cast(T*)&((cast(YAMLContainer)rhs).value_); + return (*v1).opCmp(*v2); } private: @@ -149,20 +154,21 @@ struct Node ///Equality test with another Pair. bool opEquals(const ref Pair rhs) const { - return equals!true(rhs); + return cmp!true(rhs) == 0; } private: - /* - * Equality test with another Pair. + /* + * Comparison with another Pair. * * useTag determines whether or not we consider node tags - * in the test. + * in the comparison. */ - bool equals(bool useTag)(ref const(Pair) rhs) const + int cmp(bool useTag)(ref const(Pair) rhs) const { - return key.equals!(useTag)(rhs.key) && - value.equals!(useTag)(rhs.value); + const keyCmp = key.cmp!useTag(rhs.key); + return keyCmp != 0 ? keyCmp + : value.cmp!useTag(rhs.value); } } @@ -171,6 +177,15 @@ struct Node alias Algebraic!(YAMLNull, YAMLMerge, bool, long, real, ubyte[], SysTime, string, Node.Pair[], Node[], YAMLObject) Value; + //Can Value hold this type without wrapping it in a YAMLObject? + template allowed(T) + { + enum allowed = isIntegral!T || + isFloatingPoint!T || + isSomeString!T || + Value.allowed!T; + } + private: ///Stored value. Value value_; @@ -186,8 +201,6 @@ struct Node CollectionStyle collectionStyle = CollectionStyle.Invalid; public: - @disable int opCmp(ref Node); - /** * Construct a Node from a value. * @@ -518,7 +531,7 @@ struct Node if(isType!T){return value_.get!T;} ///Must go before others, as even string/int/etc could be stored in a YAMLObject. - static if(!Value.allowed!T) if(isUserType) + static if(!allowed!T) if(isUserType) { auto object = as!YAMLObject; if(object.type is typeid(T)) @@ -569,15 +582,15 @@ struct Node //Const version of get. @property T get(T)() const if(is(T == const)) { - if(isType!T){return value_.get!T;} + if(isType!(Unqual!T)){return value_.get!T;} ///Must go before others, as even string/int/etc could be stored in a YAMLObject. - static if(!Value.allowed!T) if(isUserType) + static if(!allowed!(Unqual!T)) if(isUserType) { auto object = as!(const YAMLObject); if(object.type is typeid(T)) { - return (cast(const YAMLContainer!T)object).value_; + return (cast(const YAMLContainer!(Unqual!T))object).value_; } throw new Error("Node has unexpected type: " ~ object.type.toString ~ ". Expected: " ~ typeid(T).toString, startMark_); @@ -781,7 +794,7 @@ struct Node } /** - * Iterate over a sequence, getting each element as T. + * Foreach over a sequence, getting each element as T. * * If T is Node, simply iterate over the nodes in the sequence. * Otherwise, convert each node to T during iteration. @@ -838,7 +851,7 @@ struct Node } /** - * Iterate over a mapping, getting each key/value as K/V. + * Foreach over a mapping, getting each key/value as K/V. * * If the K and/or V is Node, simply iterate over the nodes in the mapping. * Otherwise, convert each key/value to T during iteration. @@ -1127,6 +1140,20 @@ struct Node } } + ///Compare with another _node. + const int opCmp(ref const Node node) + { + return cmp!true(node); + } + + //Compute hash of the node. + const hash_t toHash() + { + const tagHash = tag_.isNull ? 0 : tag_.toHash(); + //Variant toHash is not const at the moment, so we need to const-cast. + return tagHash + (cast(Value)value_).toHash(); + } + package: /* * Construct a node from raw data. @@ -1168,68 +1195,138 @@ struct Node { static if(is(Unqual!T == Node)) { - static if(useTag) - { - if(tag_ != rhs.tag_){return false;} - } - - if(!isValid){return !rhs.isValid;} - if(!rhs.isValid || !hasEqualType(rhs)) - { - return false; - } - - static bool compareCollection(T)(const ref Node lhs, const ref Node rhs) - { - const c1 = lhs.value_.get!(const T); - const c2 = rhs.value_.get!(const T); - if(c1 is c2){return true;} - if(c1.length != c2.length){return false;} - foreach(i; 0 .. c1.length) - { - if(!c1[i].equals!useTag(c2[i])){return false;} - } - return true; - } - - static bool compare(T)(const ref Node lhs, const ref Node rhs) - { - return lhs.value_.get!(const T) == rhs.value_.get!(const T); - } - - if(isSequence) {return compareCollection!(Node[])(this, rhs);} - else if(isMapping){return compareCollection!(Pair[])(this, rhs);} - else if(isString) {return compare!string(this, rhs);} - else if(isInt) {return compare!long(this, rhs);} - else if(isBool) {return compare!bool(this, rhs);} - else if(isBinary) {return compare!(ubyte[])(this, rhs);} - else if(isNull) {return true;} - else if(isFloat) - { - const r1 = value_.get!(const real); - const r2 = rhs.value_.get!(const real); - return isNaN(r1) ? isNaN(r2) - : (r1 <= r2 + real.epsilon && r1 >= r2 - real.epsilon); - } - else if(isTime) - { - const t1 = value_.get!(const SysTime); - const t2 = rhs.value_.get!(const SysTime); - return t1 == t2; - } - else if(isUserType) - { - return value_.get!(const YAMLObject).equals(rhs.value_.get!(const YAMLObject)); - } - assert(false, "Unknown kind of node (equality comparison) : " ~ type.toString); + return cmp!useTag(rhs) == 0; } else { - try{return rhs == get!T;} + try + { + static if(is(T == const)) + { + return rhs == get!T; + } + else + { + return rhs == get!(const(Unqual!T)); + } + } catch(NodeException e){return false;} } } + /* + * Comparison with another node. + * + * Used for ordering in mappings and for opEquals. + * + * useTag determines whether or not to consider tags in the comparison. + */ + int cmp(bool useTag)(const ref Node rhs) const + { + //Compare tags - if equal or both null, we need to compare further. + static if(useTag) + { + const tagCmp = tag_.isNull ? rhs.tag_.isNull ? 0 : -1 + : rhs.tag_.isNull ? 1 : tag_.opCmp(rhs.tag_); + if(tagCmp != 0){return tagCmp;} + } + + static int cmp(T1, T2)(T1 a, T2 b) + { + return a > b ? 1 : + a < b ? -1 : + 0; + } + + //Compare validity: if both valid, we have to compare further. + const v1 = isValid; + const v2 = rhs.isValid; + if(!v1){return v2 ? -1 : 0;} + if(!v2){return 1;} + + const typeCmp = type.opCmp(rhs.type); + if(typeCmp != 0){return typeCmp;} + + static int compareCollections(T)(const ref Node lhs, const ref Node rhs) + { + const c1 = lhs.value_.get!(const T); + const c2 = rhs.value_.get!(const T); + if(c1 is c2){return 0;} + if(c1.length != c2.length) + { + return cmp(c1.length, c2.length); + } + //Equal lengths, compare items. + foreach(i; 0 .. c1.length) + { + const itemCmp = c1[i].cmp!useTag(c2[i]); + if(itemCmp != 0){return itemCmp;} + } + return 0; + } + + if(isSequence){return compareCollections!(Node[])(this, rhs);} + if(isMapping) {return compareCollections!(Pair[])(this, rhs);} + if(isString) + { + return std.algorithm.cmp(value_.get!(const string), + rhs.value_.get!(const string)); + } + if(isInt) + { + return cmp(value_.get!(const long), rhs.value_.get!(const long)); + } + if(isBool) + { + const b1 = value_.get!(const bool); + const b2 = rhs.value_.get!(const bool); + return b1 ? b2 ? 0 : 1 + : b2 ? -1 : 0; + } + if(isBinary) + { + const b1 = value_.get!(const ubyte[]); + const b2 = rhs.value_.get!(const ubyte[]); + return std.algorithm.cmp(b1, b2); + } + if(isNull) + { + return 0; + } + //Floats need special handling for NaNs . + //We consider NaN to be lower than any float. + if(isFloat) + { + const r1 = value_.get!(const real); + const r2 = rhs.value_.get!(const real); + if(isNaN(r1)) + { + return isNaN(r2) ? 0 : -1; + } + if(isNaN(r2)) + { + return 1; + } + //Fuzzy equality. + if(r1 <= r2 + real.epsilon && r1 >= r2 - real.epsilon) + { + return 0; + } + return cmp(r1, r2); + } + else if(isTime) + { + const t1 = value_.get!(const SysTime); + const t2 = rhs.value_.get!(const SysTime); + return cmp(t1, t2); + } + else if(isUserType) + { + return value_.get!(const YAMLObject).cmp(rhs.value_.get!(const YAMLObject)); + } + assert(false, "Unknown type of node for comparison : " ~ type.toString); + } + /* * Get a string representation of the node tree. Used for debugging. * diff --git a/dyaml/representer.d b/dyaml/representer.d index 01920ab..335e533 100644 --- a/dyaml/representer.d +++ b/dyaml/representer.d @@ -16,6 +16,7 @@ module dyaml.representer; import std.algorithm; import std.array; import std.base64; +import std.container; import std.conv; import std.datetime; import std.exception; @@ -37,7 +38,7 @@ class RepresenterException : YAMLException } /** - * Represents YAML nodes of various data types as scalar, sequence and mapping nodes ready for output. + * Represents YAML nodes as scalar, sequence and mapping nodes ready for output. * * This class is used to add support for dumping of custom data types. * @@ -86,13 +87,13 @@ final class Representer representers_ = null; } - ///Set default _style for scalars. Invalid means the _style is chosen automatically. + ///Set default _style for scalars. If style is $(D ScalarStyle.Invalid), the _style is chosen automatically. @property void defaultScalarStyle(ScalarStyle style) { defaultScalarStyle_ = style; } - ///Set default _style for collections. Invalid means the _style is chosen automatically. + ///Set default _style for collections. If style is $(D CollectionStyle.Invalid), the _style is chosen automatically. @property void defaultCollectionStyle(CollectionStyle style) { defaultCollectionStyle_ = style; @@ -101,13 +102,22 @@ final class Representer /** * Add a function to represent nodes with a specific data type. * - * The representer function takes references to a Node storing the data - * type and to the Representer. It returns the represented node and may - * throw a RepresenterException. See the example for more information. + * The representer function takes references to a $(D Node) storing the data + * type and to the $(D Representer). It returns the represented node and may + * throw a $(D RepresenterException). See the example for more information. * + * * Only one function may be specified for one data type. Default data * types already have representer functions unless disabled in the - * Representer constructor. + * $(D Representer) constructor. + * + * + * Structs and classes must implement the $(D opCmp()) operator for D:YAML + * support. The signature of the operator that must be implemented + * is $(D const int opCmp(ref const MyStruct s)) for structs where + * $(I MyStruct) is the struct type, and $(D int opCmp(Object o)) for + * classes. Note that the class $(D opCmp()) should not alter the compared + * values - it is not const for compatibility reasons. * * Params: representer = Representer function to add. * @@ -122,6 +132,16 @@ final class Representer * struct MyStruct * { * int x, y, z; + * + * //Any D:YAML type must have a custom opCmp operator. + * //This is used for ordering in mappings. + * const int opCmp(ref const MyStruct s) + * { + * if(x != s.x){return x - s.x;} + * if(y != s.y){return y - s.y;} + * if(z != s.z){return z - s.z;} + * return 0; + * } * } * * Node representMyStruct(ref Node node, Representer representer) @@ -161,12 +181,16 @@ final class Representer * this.z = z; * } * - * ///We need custom opEquals for node equality, as default opEquals compares references. - * override bool opEquals(Object rhs) + * //Any D:YAML type must have a custom opCmp operator. + * //This is used for ordering in mappings. + * override int opCmp(Object o) * { - * if(typeid(rhs) != typeid(MyClass)){return false;} - * auto t = cast(MyClass)rhs; - * return x == t.x && y == t.y && z == t.z; + * MyClass s = cast(MyClass)o; + * if(s is null){return -1;} + * if(x != s.x){return x - s.x;} + * if(y != s.y){return y - s.y;} + * if(z != s.z){return z - s.z;} + * return 0; * } * * ///Useful for Node.as!string . @@ -226,6 +250,16 @@ final class Representer * struct MyStruct * { * int x, y, z; + * + * //Any D:YAML type must have a custom opCmp operator. + * //This is used for ordering in mappings. + * const int opCmp(ref const MyStruct s) + * { + * if(x != s.x){return x - s.x;} + * if(y != s.y){return y - s.y;} + * if(z != s.z){return z - s.z;} + * return 0; + * } * } * * Node representMyStruct(ref Node node, Representer representer) @@ -233,7 +267,6 @@ final class Representer * auto value = node.as!MyStruct; * auto scalar = format(value.x, ":", value.y, ":", value.z); * return representer.representScalar("!mystruct.tag", scalar); - * * } * -------------------- */ @@ -250,20 +283,30 @@ final class Representer * * This is used by representer functions that produce sequences. * - * Params: tag = Tag of the sequence. + * Params: tag = Tag of the _sequence. * sequence = Sequence of nodes. * style = Style of the _sequence. If invalid, default _style will be used. * If the node was loaded before, previous _style will always be used. * * Returns: The represented node. * - * Throws: RepresenterException if a child could not be represented. + * Throws: $(D RepresenterException) if a child could not be represented. * * Example: * -------------------- * struct MyStruct * { * int x, y, z; + * + * //Any D:YAML type must have a custom opCmp operator. + * //This is used for ordering in mappings. + * const int opCmp(ref const MyStruct s) + * { + * if(x != s.x){return x - s.x;} + * if(y != s.y){return y - s.y;} + * if(z != s.z){return z - s.z;} + * return 0; + * } * } * * Node representMyStruct(ref Node node, Representer representer) @@ -311,18 +354,28 @@ final class Representer * * Params: tag = Tag of the mapping. * pairs = Key-value _pairs of the mapping. - * style = Style of the _mapping. If invalid, default _style will be used. + * style = Style of the mapping. If invalid, default _style will be used. * If the node was loaded before, previous _style will always be used. * * Returns: The represented node. * - * Throws: RepresenterException if a child could not be represented. + * Throws: $(D RepresenterException) if a child could not be represented. * * Example: * -------------------- * struct MyStruct * { * int x, y, z; + * + * //Any D:YAML type must have a custom opCmp operator. + * //This is used for ordering in mappings. + * const int opCmp(ref const MyStruct s) + * { + * if(x != s.x){return x - s.x;} + * if(y != s.y){return y - s.y;} + * if(z != s.z){return z - s.z;} + * return 0; + * } * } * * Node representMyStruct(ref Node node, Representer representer) @@ -496,14 +549,13 @@ Node representPairs(ref Node node, Representer representer) bool hasDuplicates(Node.Pair[] pairs) { - //TODO The map here should be replaced with something with deterministic. - //memory allocation if possible. - bool[Node] map; - scope(exit){clear(map);} + //TODO this should be replaced by something with deterministic memory allocation. + auto keys = redBlackTree!Node(); + scope(exit){clear(keys);} foreach(ref pair; pairs) { - if((pair.key in map) !is null){return true;} - map[pair.key] = true; + if(pair.key in keys){return true;} + keys.insert(pair.key); } return false; } @@ -547,6 +599,14 @@ import dyaml.dumper; struct MyStruct { int x, y, z; + + const int opCmp(ref const MyStruct s) + { + if(x != s.x){return x - s.x;} + if(y != s.y){return y - s.y;} + if(z != s.z){return z - s.z;} + return 0; + } } Node representMyStruct(ref Node node, Representer representer) @@ -585,13 +645,15 @@ class MyClass this.y = y; this.z = z; } - - ///We need custom opEquals for node equality, as default opEquals compares references. - override bool opEquals(Object rhs) + + override int opCmp(Object o) { - if(typeid(rhs) != typeid(MyClass)){return false;} - auto t = cast(MyClass)rhs; - return x == t.x && y == t.y && z == t.z; + MyClass s = cast(MyClass)o; + if(s is null){return -1;} + if(x != s.x){return x - s.x;} + if(y != s.y){return y - s.y;} + if(z != s.z){return z - s.z;} + return 0; } ///Useful for Node.as!string . diff --git a/examples/constructor/main.d b/examples/constructor/main.d index 486e29e..5b00cd7 100644 --- a/examples/constructor/main.d +++ b/examples/constructor/main.d @@ -8,6 +8,14 @@ struct Color ubyte red; ubyte green; ubyte blue; + + const int opCmp(ref const Color c) + { + if(red != c.red) {return red - c.red;} + if(green != c.green){return green - c.green;} + if(blue != c.blue) {return blue - c.blue;} + return 0; + } } Color constructColorScalar(ref Node node) diff --git a/examples/representer/main.d b/examples/representer/main.d index 92b00c6..e4b96c4 100644 --- a/examples/representer/main.d +++ b/examples/representer/main.d @@ -6,6 +6,14 @@ struct Color ubyte red; ubyte green; ubyte blue; + + const int opCmp(ref const Color c) + { + if(red != c.red) {return red - c.red;} + if(green != c.green){return green - c.green;} + if(blue != c.blue) {return blue - c.blue;} + return 0; + } } Node representColor(ref Node node, Representer representer) diff --git a/examples/resolver/main.d b/examples/resolver/main.d index 65a3683..537ffa8 100644 --- a/examples/resolver/main.d +++ b/examples/resolver/main.d @@ -8,6 +8,14 @@ struct Color ubyte red; ubyte green; ubyte blue; + + const int opCmp(ref const Color c) + { + if(red != c.red) {return red - c.red;} + if(green != c.green){return green - c.green;} + if(blue != c.blue) {return blue - c.blue;} + return 0; + } } Color constructColorScalar(ref Node node) diff --git a/test/src/constructor.d b/test/src/constructor.d index 97ee866..0b67e87 100644 --- a/test/src/constructor.d +++ b/test/src/constructor.d @@ -312,11 +312,16 @@ class TestClass this.z = z; } - override bool opEquals(Object rhs) + //Any D:YAML type must have a custom opCmp operator. + //This is used for ordering in mappings. + override int opCmp(Object o) { - if(typeid(rhs) != typeid(TestClass)){return false;} - auto t = cast(TestClass)rhs; - return x == t.x && y == t.y && z == t.z; + TestClass s = cast(TestClass)o; + if(s is null){return -1;} + if(x != s.x){return x - s.x;} + if(y != s.y){return y - s.y;} + if(z != s.z){return z - s.z;} + return 0; } override string toString() @@ -330,10 +335,12 @@ struct TestStruct { int value; - bool opEquals(const ref TestStruct rhs) const + //Any D:YAML type must have a custom opCmp operator. + //This is used for ordering in mappings. + const int opCmp(ref const TestStruct s) { - return value == rhs.value; - } + return value - s.value; + } } ///Constructor function for TestClass.
string tagTag of the sequence.
Tag of the sequence.
Node[] sequence Sequence of nodes.
CollectionStyle style