diff --git a/dyaml/constructor.d b/dyaml/constructor.d index 331a49e..d860d35 100644 --- a/dyaml/constructor.d +++ b/dyaml/constructor.d @@ -183,7 +183,7 @@ final class Constructor enforce((tag in fromScalar_) !is null, new ConstructorException("Could not determine a constructor from " "scalar for tag " ~ tag.toString(), start, end)); - return Node(fromScalar_[tag](start, end, value), start, tag); + return Node.rawNode(fromScalar_[tag](start, end, value), start, tag); } /* @@ -201,7 +201,7 @@ final class Constructor enforce((tag in fromSequence_) !is null, new ConstructorException("Could not determine a constructor from " "sequence for tag " ~ tag.toString(), start, end)); - return Node(fromSequence_[tag](start, end, value), start, tag); + return Node.rawNode(fromSequence_[tag](start, end, value), start, tag); } /* @@ -219,7 +219,7 @@ final class Constructor enforce((tag in fromMapping_) !is null, new ConstructorException("Could not determine a constructor from " "mapping for tag " ~ tag.toString(), start, end)); - return Node(fromMapping_[tag](start, end, value), start, tag); + return Node.rawNode(fromMapping_[tag](start, end, value), start, tag); } } @@ -565,9 +565,9 @@ unittest Node[] pairs; foreach(long i; 0 .. length) { - auto pair = (i % 2) ? Pair(Node(Node.Value(to!string(i))), Node(Node.Value(i))) - : Pair(Node(Node.Value(i)), Node(Node.Value(to!string(i)))); - pairs ~= Node(Node.Value([pair])); + auto pair = (i % 2) ? Pair(Node.rawNode(Node.Value(to!string(i))), Node.rawNode(Node.Value(i))) + : Pair(Node.rawNode(Node.Value(i)), Node.rawNode(Node.Value(to!string(i)))); + pairs ~= Node.rawNode(Node.Value([pair])); } return pairs; } @@ -577,8 +577,8 @@ unittest Node[] pairs; foreach(long i; 0 .. length) { - auto pair = Pair(Node(Node.Value(to!string(i))), Node(Node.Value(i))); - pairs ~= Node(Node.Value([pair])); + auto pair = Pair(Node.rawNode(Node.Value(to!string(i))), Node.rawNode(Node.Value(i))); + pairs ~= Node.rawNode(Node.Value([pair])); } return pairs; } @@ -630,7 +630,7 @@ unittest Node.Pair[] pairs; foreach(long i; 0 .. length) { - pairs ~= Node.Pair(Node(Node.Value(to!string(i))), Node()); + pairs ~= Node.Pair(Node.rawNode(Node.Value(to!string(i))), Node.rawNode(Node.Value(YAMLNull()))); } return pairs; diff --git a/dyaml/node.d b/dyaml/node.d index 01d237f..6ef8dfa 100644 --- a/dyaml/node.d +++ b/dyaml/node.d @@ -143,6 +143,177 @@ struct Node Tag tag_; public: + /** + * Construct a Node from a value. + * + * Any type except of Node can be stored in a Node, but default YAML + * types (integers, floats, strings, timestamp, etc.) will be stored + * more efficiently. + * + * Note that for any non-default types you store + * in a node, you need a Representer to represent them in YAML - + * otherwise emitting will fail. + * + * Params: value = Value to store in the node. + * tag = Tag override. If specified, the tag of the node + * when emitted will be this tag, regardless of + * what Representer determines. Can be used when a + * single D data type needs to use multiple YAML tags. + */ + this(T)(T value, string tag = null) if (!isArray!T && !isAssociativeArray!T) + { + tag_ = Tag(tag); + + //No copyconstruction. + static if(is(T == Node)) + { + static assert(false); + } + //We can easily convert ints, floats, strings. + else static if(isIntegral!T) + { + value_ = Value(cast(long) value); + } + else static if(isFloatingPoint!T) + { + value_ = Value(cast(real) value); + } + else static if(isSomeString!T) + { + value_ = Value(to!string(value)); + } + //Directly supported type. + else static if(Value.allowed!T) + { + value_ = Value(value); + } + //User defined type. + else + { + value_ = userValue(value); + } + } + + /** + * Construct a node from an array_. + * + * If array is an array_ of nodes or node pairs, it is stored in the + * node directly. Otherwise, every value in the array is converted to a + * node, and those nodes are stored. + * + * Params: array = Values to store in the node. + * tag = Tag override. If specified, the tag of the node + * when emitted will be this tag, regardless of + * what Representer determines. Can be used when a + * single D data type needs to use multiple YAML tags. + * In particular, this can be used to differentiate + * between YAML sequences (!!seq) and sets (!!set), + * which both are internally represented as an array_ + * of nodes. + */ + this(T)(T[] array, string tag = null) + { + tag_ = Tag(tag); + + static if(is(T == Node) || is(T == Node.Pair)) + { + value_ = Value(array); + } + else + { + Node[] nodes; + foreach(ref value; array) + { + nodes ~= Node(value); + } + value_ = Value(nodes); + } + } + + /** + * Construct a node from an associative array_. + * + * If keys and/or values of array are nodes, they are copied to the node + * directly. Otherwise they are converted to nodes and then stored. + * + * Params: array = Values to store in the node. + * tag = Tag override. If specified, the tag of the node + * when emitted will be this tag, regardless of + * what Representer determines. Can be used when a + * single D data type needs to use multiple YAML tags. + * In particular, this can be used to differentiate + * between YAML unordered maps, (!!map) ordered maps, + * (!!omap) and pairs (!!pairs), which are all + * internally represented as an array_ of node pairs. + */ + this(K, V)(V[K] array, string tag = null) + { + tag_ = Tag(tag); + + Node.Pair[] pairs; + + Node.Pair pair; + foreach(ref key, ref value; array) + { + static if(is(K == Node)){pair.key = key;} + else{pair.key = Node(key);} + static if(is(V == Node)){pair.value = value;} + else{pair.value = Node(value);} + pairs ~= pair; + } + + value_ = Value(pairs); + } + + /** + * Construct a node from arrays of keys_ and values_. + * + * Constructs a mapping node with key-value pairs from + * keys and values, keeping their order. Useful when order + * is important (ordered maps, pairs). + * + * keys and values must have equal length. + * + * If keys_ and/or values_ of are nodes, they are copied to the node + * directly. Otherwise they are converted to nodes and then stored. + * + * Params: keys = Keys of the mapping, from first to last pair. + * values = Values of the mapping, from first to last pair. + * tag = Tag override. If specified, the tag of the node + * when emitted will be this tag, regardless of + * what Representer determines. Can be used when a + * single D data type needs to use multiple YAML tags. + * In particular, this can be used to differentiate + * between YAML unordered maps, (!!map) ordered maps, + * (!!omap) and pairs (!!pairs), which are all + * internally represented as an array_ of node pairs. + */ + this(K, V)(K[] keys, V[] values, string tag = null) + in + { + assert(keys.length == values.length, + "Lengths of keys and values arrays to construct " + "a YAML node from don't match"); + } + body + { + tag_ = Tag(tag); + + Node.Pair[] pairs; + + Node.Pair pair; + foreach(i; 0 .. keys.length) + { + static if(is(K == Node)){pair.key = keys[i];} + else{pair.key = Node(keys[i]);} + static if(is(V == Node)){pair.value = values[i];} + else{pair.value = Node(values[i]);} + pairs ~= pair; + } + + value_ = Value(pairs); + } + ///Is this node valid (initialized)? @property bool isValid() const {return value_.hasValue;} @@ -573,12 +744,17 @@ struct Node * Params: value = Value of the node. * startMark = Start position of the node in file. * tag = Tag of the node. + * + * Returns: Constructed node. */ - this(Value value, in Mark startMark = Mark(), in Tag tag = Tag("DUMMY_TAG")) + static Node rawNode(Value value, in Mark startMark = Mark(), in Tag tag = Tag("DUMMY_TAG")) { - value_ = value; - startMark_ = startMark; - tag_ = tag; + Node node; + node.value_ = value; + node.startMark_ = startMark; + node.tag_ = tag; + + return node; } /* diff --git a/test/src/constructor.d b/test/src/constructor.d index 0b79d9e..9740b88 100644 --- a/test/src/constructor.d +++ b/test/src/constructor.d @@ -54,8 +54,8 @@ static this() ///Construct a node with specified value. Node node(T)(T value) { - static if(Node.Value.allowed!T){return Node(Node.Value(value));} - else{return Node(Node.userValue(value));} + static if(Node.Value.allowed!T){return Node.rawNode(Node.Value(value));} + else{return Node.rawNode(Node.userValue(value));} } ///Construct a pair of nodes with specified values.