Implemented a Tag struct taking as little memory as possible.

Removed endMark from Node to keep it in 32 bytes on 64bit.
This will result in slightly worse debugging messages, but we
still have the start position of a node.

Tag is needed for better compliance with the spec and emitting
support for multiple tags with the same D data type.
This commit is contained in:
Ferdinand Majerech 2011-08-20 22:15:20 +02:00
parent 932c125eeb
commit 7192503fe6
6 changed files with 180 additions and 83 deletions

View file

@ -265,7 +265,7 @@ final class Composer
enforce(node.isType!(Node.Pair[]),
new ConstructorException("While constructing a mapping, " ~
"expected a mapping for merging, but found"
~ node.value_.type.toString() ~
~ node.typeString() ~
"NOTE: line/column shows topmost parent "
"to which the content is being merged",
startMark, endMark));
@ -278,7 +278,7 @@ final class Composer
throw new ConstructorException("While constructing a mapping, " ~
"expected a mapping or a list of mappings for "
"merging, but found: "
~ root.value_.type.toString() ~
~ root.typeString() ~
"NOTE: line/column shows topmost parent "
"to which the content is being merged",
startMark, endMark);

View file

@ -25,6 +25,7 @@ import std.utf;
import dyaml.node;
import dyaml.exception;
import dyaml.tag;
import dyaml.token;
@ -179,7 +180,7 @@ final class Constructor
enforce((tag in fromScalar_) !is null,
new ConstructorException("Could not determine a constructor from "
"scalar for tag " ~ tag, start, end));
return Node(fromScalar_[tag](start, end, value), start, end);
return Node(fromScalar_[tag](start, end, value), start, Tag(tag));
}
/*
@ -196,7 +197,7 @@ final class Constructor
enforce((tag in fromSequence_) !is null,
new ConstructorException("Could not determine a constructor from "
"sequence for tag " ~ tag, start, end));
return Node(fromSequence_[tag](start, end, value), start, end);
return Node(fromSequence_[tag](start, end, value), start, Tag(tag));
}
/*
@ -213,7 +214,7 @@ final class Constructor
enforce((tag in fromMapping_) !is null,
new ConstructorException("Could not determine a constructor from "
"mapping for tag " ~ tag, start, end));
return Node(fromMapping_[tag](start, end, value), start, end);
return Node(fromMapping_[tag](start, end, value), start, Tag(tag));
}
}

View file

@ -22,6 +22,7 @@ import std.variant;
import dyaml.event;
import dyaml.exception;
import dyaml.tag;
///Exception thrown at node related errors.
@ -33,11 +34,10 @@ class NodeException : YAMLException
*
* Params: msg = Error message.
* start = Start position of the node.
* end = End position of the node.
*/
this(string msg, Mark start, Mark end)
this(string msg, Mark start)
{
super(msg ~ "\nstart:" ~ start.toString() ~ "\nend:" ~ end.toString());
super(msg ~ "\nNode at:" ~ start.toString());
}
}
@ -63,7 +63,7 @@ private abstract class YAMLObject
@property TypeInfo type() const;
///Test for equality with another YAMLObject.
bool equals(YAMLObject rhs);
bool equals(const YAMLObject rhs) const;
}
//Stores a user defined YAML data type.
@ -81,13 +81,14 @@ private class YAMLContainer(T) : YAMLObject
@property override TypeInfo type() const {return typeid(T);}
//Test for equality with another YAMLObject.
override bool equals(YAMLObject rhs)
override bool equals(const YAMLObject rhs) const
{
if(rhs.type !is typeid(T)){return false;}
return value_ == (cast(YAMLContainer)rhs).value_;
}
}
/**
* YAML node.
*
@ -97,7 +98,6 @@ private class YAMLContainer(T) : YAMLObject
*/
struct Node
{
public:
///Pair of YAML nodes, used in mappings.
struct Pair
@ -113,19 +113,19 @@ struct Node
return key == rhs.key && value == rhs.value;
}
}
package:
//YAML value type.
alias Algebraic!(YAMLNull, YAMLMerge, bool, long, real, ubyte[], SysTime, string,
Node.Pair[], Node[], YAMLObject) Value;
private:
//Stored value.
Value value_;
private:
///Start position of the node.
Mark startMark_;
///End position of the node.
Mark endMark_;
///Tag of the node.
Tag tag_;
public:
///Is this node valid (initialized)?
@ -165,59 +165,7 @@ struct Node
*/
bool opEquals(T)(ref T rhs)
{
static if(is(T == Node))
{
if(!isValid){return !rhs.isValid;}
if(!rhs.isValid || (value_.type !is rhs.value_.type))
{
return false;
}
if(isSequence)
{
auto seq1 = get!(Node[]);
auto seq2 = rhs.get!(Node[]);
if(seq1.length != seq2.length){return false;}
foreach(node; 0 .. seq1.length)
{
if(seq1[node] != seq2[node]){return false;}
}
return true;
}
if(isMapping)
{
auto map1 = get!(Node.Pair[]);
auto map2 = rhs.get!(Node.Pair[]);
if(map1.length != map2.length){return false;}
foreach(pair; 0 .. map1.length)
{
if(!map1[pair].equals(map2[pair])){return false;}
}
return true;
}
if(isScalar)
{
if(isUserType)
{
if(!rhs.isUserType){return false;}
return get!YAMLObject.equals(rhs.get!YAMLObject);
}
if(isFloat)
{
if(!rhs.isFloat){return false;}
real r1 = get!real;
real r2 = rhs.get!real;
if(isNaN(r1)){return isNaN(r2);}
return r1 == r2;
}
else{return value_ == rhs.value_;}
}
assert(false, "Unknown kind of node");
}
else
{
try{return rhs == get!T;}
catch(NodeException e){return false;}
}
return valueEquals(rhs);
}
/**
@ -305,7 +253,7 @@ struct Node
catch(VariantException e)
{
throw new NodeException("Unable to convert node value to a string",
startMark_, endMark_);
startMark_);
}
}
else static if(isFloatingPoint!T)
@ -331,7 +279,7 @@ struct Node
{
throw new NodeException("Integer value out of range of type " ~
typeid(T).toString ~ "Value: " ~
to!string(temp), startMark_, endMark_);
to!string(temp), startMark_);
}
target = to!T(temp);
return;
@ -340,8 +288,8 @@ struct Node
else
{
//Can't get the value.
throw new NodeException("Node has unexpected type " ~ value_.type.toString ~
". Expected " ~ typeid(T).toString, startMark_, endMark_);
throw new NodeException("Node has unexpected type " ~ typeString ~
". Expected " ~ typeid(T).toString, startMark_);
}
}
@ -359,7 +307,7 @@ struct Node
if(isSequence) {return get!(Node[]).length;}
else if(isMapping){return get!(Pair[]).length;}
throw new NodeException("Trying to get length of a node that is not a collection",
startMark_, endMark_);
startMark_);
}
/**
@ -387,13 +335,13 @@ struct Node
auto nodes = value_.get!(Node[]);
enforce(index >= 0 && index < nodes.length,
new NodeException("Index to a sequence out of range: "
~ to!string(index), startMark_, endMark_));
~ to!string(index), startMark_));
return nodes[index];
}
else
{
throw new NodeException("Indexing a sequence with a non-integer type.",
startMark_, endMark_);
startMark_);
}
}
else if(isMapping)
@ -418,10 +366,10 @@ struct Node
}
throw new NodeException("Mapping index not found" ~
isSomeString!T ? ": " ~ to!string(index) : "",
startMark_, endMark_);
startMark_);
}
throw new NodeException("Trying to index node that does not support indexing",
startMark_, endMark_);
startMark_);
}
unittest
{
@ -465,7 +413,7 @@ struct Node
{
enforce(isSequence,
new NodeException("Trying to iterate over a node that is not a sequence",
startMark_, endMark_));
startMark_));
int result = 0;
foreach(ref node; get!(Node[]))
@ -522,7 +470,7 @@ struct Node
{
enforce(isMapping,
new NodeException("Trying to iterate over a node that is not a mapping",
startMark_, endMark_));
startMark_));
int result = 0;
foreach(ref pair; get!(Node.Pair[]))
@ -604,6 +552,78 @@ struct Node
}
package:
/*
* Construct a node from raw data.
*
* Params: value = Value of the node.
* startMark = Start position of the node in file.
* tag = Tag of the node.
*/
this(Value value, in Mark startMark = Mark(), in Tag tag = Tag("DUMMY_TAG"))
{
value_ = value;
startMark_ = startMark;
tag_ = tag;
}
///Equality test without taking tag into account.
bool valueEquals(T)(ref T rhs)
{
static if(is(T == Node))
{
if(!isValid){return !rhs.isValid;}
if(!rhs.isValid || !hasEqualType(rhs))
{
return false;
}
if(isSequence)
{
auto seq1 = get!(Node[]);
auto seq2 = rhs.get!(Node[]);
if(seq1.length != seq2.length){return false;}
foreach(node; 0 .. seq1.length)
{
if(seq1[node] != seq2[node]){return false;}
}
return true;
}
if(isMapping)
{
auto map1 = get!(Node.Pair[]);
auto map2 = rhs.get!(Node.Pair[]);
if(map1.length != map2.length){return false;}
foreach(pair; 0 .. map1.length)
{
if(!map1[pair].equals(map2[pair])){return false;}
}
return true;
}
if(isScalar)
{
if(isUserType)
{
if(!rhs.isUserType){return false;}
return get!YAMLObject.equals(rhs.get!YAMLObject);
}
if(isFloat)
{
if(!rhs.isFloat){return false;}
real r1 = get!real;
real r2 = rhs.get!real;
if(isNaN(r1)){return isNaN(r2);}
return r1 == r2;
}
else{return value_ == rhs.value_;}
}
assert(false, "Unknown kind of node");
}
else
{
try{return rhs == get!T;}
catch(NodeException e){return false;}
}
}
/*
* Get a string representation of the node tree. Used for debugging.
*
@ -641,7 +661,7 @@ struct Node
if(isScalar)
{
return indent ~ "scalar(" ~
(convertsTo!string ? get!string : value_.type.toString) ~ ")\n";
(convertsTo!string ? get!string : typeString) ~ ")\n";
}
assert(false);
}
@ -652,6 +672,9 @@ struct Node
return Value(cast(YAMLObject)new YAMLContainer!T(value));
}
//Return string representation of the type of the node.
@property string typeString() const {return to!string(value_.type);}
private:
/*
* Determine if the value stored by the node is of specified type.
@ -666,6 +689,12 @@ struct Node
///Is the value a floating point number of some kind?
alias isType!real isFloat;
///Does given node have the same type as this node?
bool hasEqualType(ref Node node)
{
return value_.type is node.value_.type;
}
//Determine if the value can be converted to specified type.
bool convertsTo(T)()
{

55
dyaml/tag.d Normal file
View file

@ -0,0 +1,55 @@
// Copyright Ferdinand Majerech 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
///YAML tag.
module dyaml.tag;
import core.sync.mutex;
///YAML tag (data type) struct. Encapsulates a tag to save memory and speed-up comparison.
struct Tag
{
private:
///Index of the tag in tags_.
uint index_;
/**
* All known tags are in this array.
*
* Note that this is not shared among threads.
* Working the same YAML file in multiple threads is NOT safe with D:YAML.
*/
static string[] tags_;
public:
///Construct a tag from a string representation.
this(string tag)
{
foreach(uint index, knownTag; tags_)
{
if(tag == knownTag)
{
index_ = index;
return;
}
}
index_ = cast(uint)tags_.length;
tags_ ~= tag;
}
///Get string representation of the tag.
string toString() const
{
return tags_[index_];
}
///Test for equality with another tag.
bool opEquals(const ref Tag tag) const
{
return tag.index_ == index_;
}
}

View file

@ -45,10 +45,21 @@ void testLoader(bool verbose, string dataFilename, string canonicalFilename)
auto data = loadAll(dataFilename);
auto canonical = loadAll(canonicalFilename);
assert(data.length == canonical.length);
assert(data.length == canonical.length, "Unequal node count");
foreach(n; 0 .. data.length)
{
assert(data[n] == canonical[n]);
if(data[n] != canonical[n])
{
if(verbose)
{
writeln("Normal value:");
writeln(data[n].debugString);
writeln("\n");
writeln("Canonical value:");
writeln(canonical[n].debugString);
}
assert(false);
}
}
}

View file

@ -11,6 +11,7 @@ import std.datetime;
import std.exception;
import std.path;
import dyaml.tag;
import dyaml.testcommon;
@ -383,7 +384,7 @@ void testConstructor(bool verbose, string dataFilename, string codeDummy)
size_t i = 0;
foreach(node; loader)
{
if(node != expected[base][i])
if(!node.valueEquals(expected[base][i]))
{
if(verbose)
{