aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ext_depends/D-YAML/source
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext_depends/D-YAML/source')
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/composer.d82
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/constructor.d323
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/dumper.d3
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/emitter.d8
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/event.d152
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/exception.d222
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/loader.d175
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/node.d16
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/parser.d51
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/queue.d5
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/reader.d327
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/representer.d5
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/resolver.d42
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/scanner.d430
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/test/common.d223
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/test/compare.d51
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/test/constructor.d40
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/test/emitter.d132
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/test/errors.d64
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/test/inputoutput.d92
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/test/reader.d37
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/test/representer.d10
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/test/resolver.d39
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/test/suite.d384
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/test/suitehelpers.d224
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/test/tokens.d93
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/token.d2
27 files changed, 1432 insertions, 1800 deletions
diff --git a/src/ext_depends/D-YAML/source/dyaml/composer.d b/src/ext_depends/D-YAML/source/dyaml/composer.d
index e7b083a..5467af8 100644
--- a/src/ext_depends/D-YAML/source/dyaml/composer.d
+++ b/src/ext_depends/D-YAML/source/dyaml/composer.d
@@ -29,15 +29,6 @@ import dyaml.resolver;
package:
-/**
- * Exception thrown at composer errors.
- *
- * See_Also: MarkedYAMLException
- */
-class ComposerException : MarkedYAMLException
-{
- mixin MarkedExceptionCtors;
-}
///Composes YAML documents from events provided by a Parser.
struct Composer
@@ -70,7 +61,7 @@ struct Composer
* Params: parser = Parser to provide YAML events.
* resolver = Resolver to resolve tags (data types).
*/
- this(Parser parser, Resolver resolver) @safe
+ this(Parser parser, Resolver resolver) @safe nothrow
{
parser_ = parser;
resolver_ = resolver;
@@ -101,6 +92,22 @@ struct Composer
return composeDocument();
}
+ /// Set file name.
+ ref inout(string) name() inout @safe return pure nothrow @nogc
+ {
+ return parser_.name;
+ }
+ /// Get a mark from the current reader position
+ Mark mark() const @safe pure nothrow @nogc
+ {
+ return parser_.mark;
+ }
+
+ /// Get resolver
+ ref Resolver resolver() @safe return pure nothrow @nogc {
+ return resolver_;
+ }
+
private:
void skipExpected(const EventID id) @safe
@@ -158,8 +165,8 @@ struct Composer
//it's not finished, i.e. we're currently composing it
//and trying to use it recursively here.
enforce(anchors_[anchor] != Node(),
- new ComposerException("Found recursive alias: " ~ anchor,
- event.startMark));
+ new ComposerException(text("Found recursive alias: ", anchor),
+ event.startMark, "defined here", anchors_[anchor].startMark));
return anchors_[anchor];
}
@@ -168,8 +175,8 @@ struct Composer
const anchor = event.anchor;
if((anchor !is null) && (anchor in anchors_) !is null)
{
- throw new ComposerException("Found duplicate anchor: " ~ anchor,
- event.startMark);
+ throw new ComposerException(text("Found duplicate anchor: ", anchor),
+ event.startMark, "defined here", anchors_[anchor].startMark);
}
Node result;
@@ -177,7 +184,9 @@ struct Composer
//used to detect duplicate and recursive anchors.
if(anchor !is null)
{
- anchors_[anchor] = Node();
+ Node tempNode;
+ tempNode.startMark_ = event.startMark;
+ anchors_[anchor] = tempNode;
}
switch (parser_.front.id)
@@ -265,12 +274,10 @@ struct Composer
{
//this is Composer, but the code is related to Constructor.
throw new ConstructorException("While constructing a mapping, " ~
- "expected a mapping or a list of " ~
- "mappings for merging, but found: " ~
- text(node.type) ~
- " NOTE: line/column shows topmost parent " ~
- "to which the content is being merged",
- startMark, endMark);
+ "expected a mapping or a list of " ~
+ "mappings for merging, but found: " ~
+ text(node.type),
+ endMark, "mapping started here", startMark);
}
ensureAppendersExist(pairAppenderLevel, nodeAppenderLevel);
@@ -360,14 +367,14 @@ struct Composer
}
auto sorted = pairAppender.data.dup.sort!((x,y) => x.key > y.key);
- if (sorted.length) {
+ if (sorted.length)
+ {
foreach (index, const ref value; sorted[0 .. $ - 1].enumerate)
- if (value.key == sorted[index + 1].key) {
- const message = () @trusted {
- return format("Key '%s' appears multiple times in mapping (first: %s)",
- value.key.get!string, value.key.startMark);
- }();
- throw new ComposerException(message, sorted[index + 1].key.startMark);
+ if (value.key == sorted[index + 1].key)
+ {
+ throw new ComposerException(
+ text("Key '", value.key.get!string, "' appears multiple times in mapping"),
+ sorted[index + 1].key.startMark, "defined here", value.key.startMark);
}
}
@@ -380,22 +387,3 @@ struct Composer
return node;
}
}
-
-// Provide good error message on multiple keys (which JSON supports)
-@safe unittest
-{
- import dyaml.loader : Loader;
-
- const str = `{
- "comment": "This is a common technique",
- "name": "foobar",
- "comment": "To write down comments pre-JSON5"
-}`;
-
- try
- auto node = Loader.fromString(str).load();
- catch (ComposerException exc)
- assert(exc.message() ==
- "Key 'comment' appears multiple times in mapping " ~
- "(first: file <unknown>,line 2,column 5)\nfile <unknown>,line 4,column 5");
-}
diff --git a/src/ext_depends/D-YAML/source/dyaml/constructor.d b/src/ext_depends/D-YAML/source/dyaml/constructor.d
index 4cd1546..72c32f4 100644
--- a/src/ext_depends/D-YAML/source/dyaml/constructor.d
+++ b/src/ext_depends/D-YAML/source/dyaml/constructor.d
@@ -30,22 +30,6 @@ import dyaml.style;
package:
-// Exception thrown at constructor errors.
-class ConstructorException : YAMLException
-{
- /// Construct a ConstructorException.
- ///
- /// Params: msg = Error message.
- /// start = Start position of the error context.
- /// end = End position of the error context.
- this(string msg, Mark start, Mark end, string file = __FILE__, size_t line = __LINE__)
- @safe pure nothrow
- {
- super(msg ~ "\nstart: " ~ start.toString() ~ "\nend: " ~ end.toString(),
- file, line);
- }
-}
-
/** Constructs YAML values.
*
* Each YAML scalar, sequence or mapping has a tag specifying its data type.
@@ -76,109 +60,106 @@ Node constructNode(T)(const Mark start, const Mark end, const string tag,
if((is(T : string) || is(T == Node[]) || is(T == Node.Pair[])))
{
Node newNode;
- try
+ noreturn error(string a, string b)()
{
- switch(tag)
- {
- case "tag:yaml.org,2002:null":
- newNode = Node(YAMLNull(), tag);
+ enum msg = "Error constructing " ~ T.stringof ~ ": Only " ~ a ~ " can be " ~ b;
+ throw new ConstructorException(msg, start, "end", end);
+ }
+ switch(tag)
+ {
+ case "tag:yaml.org,2002:null":
+ newNode = Node(YAMLNull(), tag);
+ break;
+ case "tag:yaml.org,2002:bool":
+ static if(is(T == string))
+ {
+ newNode = Node(constructBool(value, start, end), tag);
break;
- case "tag:yaml.org,2002:bool":
- static if(is(T == string))
- {
- newNode = Node(constructBool(value), tag);
- break;
- }
- else throw new Exception("Only scalars can be bools");
- case "tag:yaml.org,2002:int":
- static if(is(T == string))
- {
- newNode = Node(constructLong(value), tag);
- break;
- }
- else throw new Exception("Only scalars can be ints");
- case "tag:yaml.org,2002:float":
- static if(is(T == string))
- {
- newNode = Node(constructReal(value), tag);
- break;
- }
- else throw new Exception("Only scalars can be floats");
- case "tag:yaml.org,2002:binary":
- static if(is(T == string))
- {
- newNode = Node(constructBinary(value), tag);
- break;
- }
- else throw new Exception("Only scalars can be binary data");
- case "tag:yaml.org,2002:timestamp":
- static if(is(T == string))
- {
- newNode = Node(constructTimestamp(value), tag);
- break;
- }
- else throw new Exception("Only scalars can be timestamps");
- case "tag:yaml.org,2002:str":
- static if(is(T == string))
- {
- newNode = Node(constructString(value), tag);
- break;
- }
- else throw new Exception("Only scalars can be strings");
- case "tag:yaml.org,2002:value":
- static if(is(T == string))
- {
- newNode = Node(constructString(value), tag);
- break;
- }
- else throw new Exception("Only scalars can be values");
- case "tag:yaml.org,2002:omap":
- static if(is(T == Node[]))
- {
- newNode = Node(constructOrderedMap(value), tag);
- break;
- }
- else throw new Exception("Only sequences can be ordered maps");
- case "tag:yaml.org,2002:pairs":
- static if(is(T == Node[]))
- {
- newNode = Node(constructPairs(value), tag);
- break;
- }
- else throw new Exception("Only sequences can be pairs");
- case "tag:yaml.org,2002:set":
- static if(is(T == Node.Pair[]))
- {
- newNode = Node(constructSet(value), tag);
- break;
- }
- else throw new Exception("Only mappings can be sets");
- case "tag:yaml.org,2002:seq":
- static if(is(T == Node[]))
- {
- newNode = Node(constructSequence(value), tag);
- break;
- }
- else throw new Exception("Only sequences can be sequences");
- case "tag:yaml.org,2002:map":
- static if(is(T == Node.Pair[]))
- {
- newNode = Node(constructMap(value), tag);
- break;
- }
- else throw new Exception("Only mappings can be maps");
- case "tag:yaml.org,2002:merge":
- newNode = Node(YAMLMerge(), tag);
+ }
+ else error!("scalars", "bools");
+ case "tag:yaml.org,2002:int":
+ static if(is(T == string))
+ {
+ newNode = Node(constructLong(value, start, end), tag);
break;
- default:
- newNode = Node(value, tag);
+ }
+ else error!("scalars", "ints");
+ case "tag:yaml.org,2002:float":
+ static if(is(T == string))
+ {
+ newNode = Node(constructReal(value, start, end), tag);
break;
- }
- }
- catch(Exception e)
- {
- throw new ConstructorException("Error constructing " ~ typeid(T).toString()
- ~ ":\n" ~ e.msg, start, end);
+ }
+ else error!("scalars", "floats");
+ case "tag:yaml.org,2002:binary":
+ static if(is(T == string))
+ {
+ newNode = Node(constructBinary(value, start, end), tag);
+ break;
+ }
+ else error!("scalars", "binary data");
+ case "tag:yaml.org,2002:timestamp":
+ static if(is(T == string))
+ {
+ newNode = Node(constructTimestamp(value, start, end), tag);
+ break;
+ }
+ else error!("scalars", "timestamps");
+ case "tag:yaml.org,2002:str":
+ static if(is(T == string))
+ {
+ newNode = Node(constructString(value, start, end), tag);
+ break;
+ }
+ else error!("scalars", "strings");
+ case "tag:yaml.org,2002:value":
+ static if(is(T == string))
+ {
+ newNode = Node(constructString(value, start, end), tag);
+ break;
+ }
+ else error!("scalars", "values");
+ case "tag:yaml.org,2002:omap":
+ static if(is(T == Node[]))
+ {
+ newNode = Node(constructOrderedMap(value, start, end), tag);
+ break;
+ }
+ else error!("sequences", "ordered maps");
+ case "tag:yaml.org,2002:pairs":
+ static if(is(T == Node[]))
+ {
+ newNode = Node(constructPairs(value, start, end), tag);
+ break;
+ }
+ else error!("sequences", "pairs");
+ case "tag:yaml.org,2002:set":
+ static if(is(T == Node.Pair[]))
+ {
+ newNode = Node(constructSet(value, start, end), tag);
+ break;
+ }
+ else error!("mappings", "sets");
+ case "tag:yaml.org,2002:seq":
+ static if(is(T == Node[]))
+ {
+ newNode = Node(constructSequence(value, start, end), tag);
+ break;
+ }
+ else error!("sequences", "sequences");
+ case "tag:yaml.org,2002:map":
+ static if(is(T == Node.Pair[]))
+ {
+ newNode = Node(constructMap(value, start, end), tag);
+ break;
+ }
+ else error!("mappings", "maps");
+ case "tag:yaml.org,2002:merge":
+ newNode = Node(YAMLMerge(), tag);
+ break;
+ default:
+ newNode = Node(value, tag);
+ break;
}
newNode.startMark_ = start;
@@ -188,16 +169,21 @@ Node constructNode(T)(const Mark start, const Mark end, const string tag,
private:
// Construct a boolean _node.
-bool constructBool(const string str) @safe
+bool constructBool(const string str, const Mark start, const Mark end) @safe
{
string value = str.toLower();
if(value.among!("yes", "true", "on")){return true;}
if(value.among!("no", "false", "off")){return false;}
- throw new Exception("Unable to parse boolean value: " ~ value);
+ throw new ConstructorException("Invalid boolean value: " ~ str, start, "ending at", end);
+}
+
+@safe unittest
+{
+ assert(collectException!ConstructorException(constructBool("foo", Mark("unittest", 1, 0), Mark("unittest", 1, 3))).msg == "Invalid boolean value: foo");
}
// Construct an integer (long) _node.
-long constructLong(const string str) @safe
+long constructLong(const string str, const Mark start, const Mark end) @safe
{
string value = str.replace("_", "");
const char c = value[0];
@@ -207,7 +193,7 @@ long constructLong(const string str) @safe
value = value[1 .. $];
}
- enforce(value != "", new Exception("Unable to parse float value: " ~ value));
+ enforce(value != "", new ConstructorException("Unable to parse integer value: " ~ str, start, "ending at", end));
long result;
try
@@ -237,7 +223,7 @@ long constructLong(const string str) @safe
}
catch(ConvException e)
{
- throw new Exception("Unable to parse integer value: " ~ value);
+ throw new ConstructorException("Unable to parse integer value: " ~ str, start, "ending at", end);
}
return result;
@@ -251,16 +237,18 @@ long constructLong(const string str) @safe
string binary = "0b1010_0111_0100_1010_1110";
string sexagesimal = "190:20:30";
- assert(685230 == constructLong(canonical));
- assert(685230 == constructLong(decimal));
- assert(685230 == constructLong(octal));
- assert(685230 == constructLong(hexadecimal));
- assert(685230 == constructLong(binary));
- assert(685230 == constructLong(sexagesimal));
+ assert(685230 == constructLong(canonical, Mark.init, Mark.init));
+ assert(685230 == constructLong(decimal, Mark.init, Mark.init));
+ assert(685230 == constructLong(octal, Mark.init, Mark.init));
+ assert(685230 == constructLong(hexadecimal, Mark.init, Mark.init));
+ assert(685230 == constructLong(binary, Mark.init, Mark.init));
+ assert(685230 == constructLong(sexagesimal, Mark.init, Mark.init));
+ assert(collectException!ConstructorException(constructLong("+", Mark.init, Mark.init)).msg == "Unable to parse integer value: +");
+ assert(collectException!ConstructorException(constructLong("0xINVALID", Mark.init, Mark.init)).msg == "Unable to parse integer value: 0xINVALID");
}
// Construct a floating point (real) _node.
-real constructReal(const string str) @safe
+real constructReal(const string str, const Mark start, const Mark end) @safe
{
string value = str.replace("_", "").toLower();
const char c = value[0];
@@ -271,7 +259,7 @@ real constructReal(const string str) @safe
}
enforce(value != "" && value != "nan" && value != "inf" && value != "-inf",
- new Exception("Unable to parse float value: " ~ value));
+ new ConstructorException("Unable to parse float value: \"" ~ str ~ "\"", start, "ending at", end));
real result;
try
@@ -297,7 +285,7 @@ real constructReal(const string str) @safe
}
catch(ConvException e)
{
- throw new Exception("Unable to parse float value: \"" ~ value ~ "\"");
+ throw new ConstructorException("Unable to parse float value: \"" ~ str ~ "\"", start, "ending at", end);
}
return result;
@@ -316,16 +304,18 @@ real constructReal(const string str) @safe
string negativeInf = "-.inf";
string NaN = ".NaN";
- assert(eq(685230.15, constructReal(canonical)));
- assert(eq(685230.15, constructReal(exponential)));
- assert(eq(685230.15, constructReal(fixed)));
- assert(eq(685230.15, constructReal(sexagesimal)));
- assert(eq(-real.infinity, constructReal(negativeInf)));
- assert(to!string(constructReal(NaN)) == "nan");
+ assert(eq(685230.15, constructReal(canonical, Mark.init, Mark.init)));
+ assert(eq(685230.15, constructReal(exponential, Mark.init, Mark.init)));
+ assert(eq(685230.15, constructReal(fixed, Mark.init, Mark.init)));
+ assert(eq(685230.15, constructReal(sexagesimal, Mark.init, Mark.init)));
+ assert(eq(-real.infinity, constructReal(negativeInf, Mark.init, Mark.init)));
+ assert(to!string(constructReal(NaN, Mark.init, Mark.init)) == "nan");
+ assert(collectException!ConstructorException(constructReal("+", Mark.init, Mark.init)).msg == "Unable to parse float value: \"+\"");
+ assert(collectException!ConstructorException(constructReal("74.invalid", Mark.init, Mark.init)).msg == "Unable to parse float value: \"74.invalid\"");
}
// Construct a binary (base64) _node.
-ubyte[] constructBinary(const string value) @safe
+ubyte[] constructBinary(const string value, const Mark start, const Mark end) @safe
{
import std.ascii : newline;
import std.array : array;
@@ -337,7 +327,7 @@ ubyte[] constructBinary(const string value) @safe
}
catch(Base64Exception e)
{
- throw new Exception("Unable to decode base64 value: " ~ e.msg);
+ throw new ConstructorException("Unable to decode base64 value: " ~ e.msg, start, "ending at", end);
}
}
@@ -347,13 +337,13 @@ ubyte[] constructBinary(const string value) @safe
char[] buffer;
buffer.length = 256;
string input = Base64.encode(test, buffer).idup;
- const value = constructBinary(input);
+ const value = constructBinary(input, Mark.init, Mark.init);
assert(value == test);
assert(value == [84, 104, 101, 32, 65, 110, 115, 119, 101, 114, 58, 32, 52, 50]);
}
// Construct a timestamp (SysTime) _node.
-SysTime constructTimestamp(const string str) @safe
+SysTime constructTimestamp(const string str, const Mark start, const Mark end) @safe
{
string value = str;
@@ -429,7 +419,7 @@ SysTime constructTimestamp(const string str) @safe
{
string timestamp(string value)
{
- return constructTimestamp(value).toISOString();
+ return constructTimestamp(value, Mark.init, Mark.init).toISOString();
}
string canonical = "2001-12-15T02:59:43.1Z";
@@ -452,21 +442,21 @@ SysTime constructTimestamp(const string str) @safe
}
// Construct a string _node.
-string constructString(const string str) @safe
+string constructString(const string str, const Mark start, const Mark end) @safe
{
return str;
}
// Convert a sequence of single-element mappings into a sequence of pairs.
-Node.Pair[] getPairs(string type, const Node[] nodes) @safe
+Node.Pair[] getPairs(string type)(const Node[] nodes) @safe
{
+ enum msg = "While constructing " ~ type ~ ", expected a mapping with single element";
Node.Pair[] pairs;
pairs.reserve(nodes.length);
foreach(node; nodes)
{
enforce(node.nodeID == NodeID.mapping && node.length == 1,
- new Exception("While constructing " ~ type ~
- ", expected a mapping with single element"));
+ new ConstructorException(msg, node.startMark));
pairs ~= node.as!(Node.Pair[]);
}
@@ -475,30 +465,33 @@ Node.Pair[] getPairs(string type, const Node[] nodes) @safe
}
// Construct an ordered map (ordered sequence of key:value pairs without duplicates) _node.
-Node.Pair[] constructOrderedMap(const Node[] nodes) @safe
+Node.Pair[] constructOrderedMap(const Node[] nodes, const Mark start, const Mark end) @safe
{
- auto pairs = getPairs("ordered map", nodes);
+ auto pairs = getPairs!"an ordered map"(nodes);
//Detect duplicates.
//TODO this should be replaced by something with deterministic memory allocation.
auto keys = new RedBlackTree!Node();
foreach(ref pair; pairs)
{
- enforce(!(pair.key in keys),
- new Exception("Duplicate entry in an ordered map: "
- ~ pair.key.debugString()));
+ auto foundMatch = keys.equalRange(pair.key);
+ enforce(foundMatch.empty, new ConstructorException(
+ "Duplicate entry in an ordered map", pair.key.startMark,
+ "first occurrence here", foundMatch.front.startMark));
keys.insert(pair.key);
}
return pairs;
}
@safe unittest
{
+ uint lines;
Node[] alternateTypes(uint length) @safe
{
Node[] pairs;
foreach(long i; 0 .. length)
{
auto pair = (i % 2) ? Node.Pair(i.to!string, i) : Node.Pair(i, i.to!string);
+ pair.key.startMark_ = Mark("unittest", lines++, 0);
pairs ~= Node([pair]);
}
return pairs;
@@ -510,27 +503,29 @@ Node.Pair[] constructOrderedMap(const Node[] nodes) @safe
foreach(long i; 0 .. length)
{
auto pair = Node.Pair(i.to!string, i);
+ pair.key.startMark_ = Mark("unittest", lines++, 0);
pairs ~= Node([pair]);
}
return pairs;
}
- assertThrown(constructOrderedMap(alternateTypes(8) ~ alternateTypes(2)));
- assertNotThrown(constructOrderedMap(alternateTypes(8)));
- assertThrown(constructOrderedMap(sameType(64) ~ sameType(16)));
- assertThrown(constructOrderedMap(alternateTypes(64) ~ alternateTypes(16)));
- assertNotThrown(constructOrderedMap(sameType(64)));
- assertNotThrown(constructOrderedMap(alternateTypes(64)));
+ assert(collectException!ConstructorException(constructOrderedMap(alternateTypes(8) ~ alternateTypes(2), Mark.init, Mark.init)).message == "Duplicate entry in an ordered map\nunittest:9,1\nfirst occurrence here: unittest:1,1");
+ assertNotThrown(constructOrderedMap(alternateTypes(8), Mark.init, Mark.init));
+ assert(collectException!ConstructorException(constructOrderedMap(sameType(64) ~ sameType(16), Mark.init, Mark.init)).message == "Duplicate entry in an ordered map\nunittest:83,1\nfirst occurrence here: unittest:19,1");
+ assert(collectException!ConstructorException(constructOrderedMap(alternateTypes(64) ~ alternateTypes(16), Mark.init, Mark.init)).message == "Duplicate entry in an ordered map\nunittest:163,1\nfirst occurrence here: unittest:99,1");
+ assertNotThrown(constructOrderedMap(sameType(64), Mark.init, Mark.init));
+ assertNotThrown(constructOrderedMap(alternateTypes(64), Mark.init, Mark.init));
+ assert(collectException!ConstructorException(constructOrderedMap([Node([Node(1), Node(2)])], Mark.init, Mark.init)).message == "While constructing an ordered map, expected a mapping with single element\n<unknown>:1,1");
}
// Construct a pairs (ordered sequence of key: value pairs allowing duplicates) _node.
-Node.Pair[] constructPairs(const Node[] nodes) @safe
+Node.Pair[] constructPairs(const Node[] nodes, const Mark start, const Mark end) @safe
{
- return getPairs("pairs", nodes);
+ return getPairs!"pairs"(nodes);
}
// Construct a set _node.
-Node[] constructSet(const Node.Pair[] pairs) @safe
+Node[] constructSet(const Node.Pair[] pairs, const Mark start, const Mark end) @safe
{
// In future, the map here should be replaced with something with deterministic
// memory allocation if possible.
@@ -583,20 +578,20 @@ Node[] constructSet(const Node.Pair[] pairs) @safe
auto nodeDuplicatesLong = DuplicatesLong.dup;
auto nodeNoDuplicatesLong = noDuplicatesLong.dup;
- assertThrown(constructSet(nodeDuplicatesShort));
- assertNotThrown(constructSet(nodeNoDuplicatesShort));
- assertThrown(constructSet(nodeDuplicatesLong));
- assertNotThrown(constructSet(nodeNoDuplicatesLong));
+ assertThrown(constructSet(nodeDuplicatesShort, Mark.init, Mark.init));
+ assertNotThrown(constructSet(nodeNoDuplicatesShort, Mark.init, Mark.init));
+ assertThrown(constructSet(nodeDuplicatesLong, Mark.init, Mark.init));
+ assertNotThrown(constructSet(nodeNoDuplicatesLong, Mark.init, Mark.init));
}
// Construct a sequence (array) _node.
-Node[] constructSequence(Node[] nodes) @safe
+Node[] constructSequence(Node[] nodes, const Mark start, const Mark end) @safe
{
return nodes;
}
// Construct an unordered map (unordered set of key:value _pairs without duplicates) _node.
-Node.Pair[] constructMap(Node.Pair[] pairs) @safe
+Node.Pair[] constructMap(Node.Pair[] pairs, const Mark start, const Mark end) @safe
{
//Detect duplicates.
//TODO this should be replaced by something with deterministic memory allocation.
diff --git a/src/ext_depends/D-YAML/source/dyaml/dumper.d b/src/ext_depends/D-YAML/source/dyaml/dumper.d
index 03d3620..e4aa1d3 100644
--- a/src/ext_depends/D-YAML/source/dyaml/dumper.d
+++ b/src/ext_depends/D-YAML/source/dyaml/dumper.d
@@ -73,9 +73,6 @@ struct Dumper
// Default style for collection nodes. If style is $(D CollectionStyle.invalid), the _style is chosen automatically.
CollectionStyle defaultCollectionStyle = CollectionStyle.invalid;
- @disable bool opEquals(ref Dumper);
- @disable int opCmp(ref Dumper);
-
///Set indentation width. 2 by default. Must not be zero.
@property void indent(uint indent) pure @safe nothrow
in
diff --git a/src/ext_depends/D-YAML/source/dyaml/emitter.d b/src/ext_depends/D-YAML/source/dyaml/emitter.d
index 5aafc0e..b61fea1 100644
--- a/src/ext_depends/D-YAML/source/dyaml/emitter.d
+++ b/src/ext_depends/D-YAML/source/dyaml/emitter.d
@@ -153,9 +153,6 @@ struct Emitter(Range, CharType) if (isOutputRange!(Range, CharType))
ScalarStyle style_ = ScalarStyle.invalid;
public:
- @disable int opCmp(ref Emitter);
- @disable bool opEquals(ref Emitter);
-
/**
* Construct an emitter.
*
@@ -775,7 +772,7 @@ struct Emitter(Range, CharType) if (isOutputRange!(Range, CharType))
{
if(style_ == ScalarStyle.invalid){style_ = chooseScalarStyle();}
if((!canonical_ || (tag is null)) &&
- ((tag == "tag:yaml.org,2002:str") || (style_ == ScalarStyle.plain ? event_.implicit : !event_.implicit && (tag is null))))
+ (((tag == "tag:yaml.org,2002:str") && event_.implicit) || (style_ == ScalarStyle.plain ? event_.implicit : !event_.implicit && (tag is null))))
{
preparedTag_ = null;
return;
@@ -1283,9 +1280,6 @@ struct ScalarWriter(Range, CharType)
}
private:
- @disable int opCmp(ref Emitter!(Range, CharType));
- @disable bool opEquals(ref Emitter!(Range, CharType));
-
///Used as "null" UTF-32 character.
static immutable dcharNone = dchar.max;
diff --git a/src/ext_depends/D-YAML/source/dyaml/event.d b/src/ext_depends/D-YAML/source/dyaml/event.d
index f4a747f..36638e3 100644
--- a/src/ext_depends/D-YAML/source/dyaml/event.d
+++ b/src/ext_depends/D-YAML/source/dyaml/event.d
@@ -10,6 +10,7 @@
*/
module dyaml.event;
+import std.algorithm;
import std.array;
import std.conv;
@@ -43,42 +44,30 @@ enum EventID : ubyte
*/
struct Event
{
- @disable int opCmp(ref Event);
-
///Value of the event, if any.
string value;
///Start position of the event in file/stream.
Mark startMark;
///End position of the event in file/stream.
Mark endMark;
- union
- {
- struct
- {
- ///Anchor of the event, if any.
- string _anchor;
- ///Tag of the event, if any.
- string _tag;
- }
- ///Tag directives, if this is a DocumentStart.
- //TagDirectives tagDirectives;
- TagDirective[] _tagDirectives;
- }
+ ///Anchor of the event, if any.
+ string _anchor;
+ ///Tag of the event, if any.
+ string _tag;
+ ///Tag directives, if this is a DocumentStart.
+ TagDirective[] _tagDirectives;
///Event type.
EventID id = EventID.invalid;
///Style of scalar event, if this is a scalar event.
ScalarStyle scalarStyle = ScalarStyle.invalid;
- union
- {
- ///Should the tag be implicitly resolved?
- bool implicit;
- /**
- * Is this document event explicit?
- *
- * Used if this is a DocumentStart or DocumentEnd.
- */
- bool explicitDocument;
- }
+ ///Should the tag be implicitly resolved?
+ bool implicit;
+ /**
+ * Is this document event explicit?
+ *
+ * Used if this is a DocumentStart or DocumentEnd.
+ */
+ alias explicitDocument = implicit;
///Collection style, if this is a SequenceStart or MappingStart.
CollectionStyle collectionStyle = CollectionStyle.invalid;
@@ -102,6 +91,117 @@ struct Event
assert(id == EventID.documentStart, "Only DocumentStart events have tag directives.");
return _tagDirectives;
}
+ void toString(W)(ref W writer) const
+ {
+ import std.algorithm.iteration : substitute;
+ import std.format : formattedWrite;
+ import std.range : put;
+ final switch (id)
+ {
+ case EventID.scalar:
+ put(writer, "=VAL ");
+ if (anchor != "")
+ {
+ writer.formattedWrite!"&%s " (anchor);
+ }
+ if (tag != "")
+ {
+ writer.formattedWrite!"<%s> " (tag);
+ }
+ final switch(scalarStyle)
+ {
+ case ScalarStyle.singleQuoted:
+ put(writer, "'");
+ break;
+ case ScalarStyle.doubleQuoted:
+ put(writer, "\"");
+ break;
+ case ScalarStyle.literal:
+ put(writer, "|");
+ break;
+ case ScalarStyle.folded:
+ put(writer, ">");
+ break;
+ case ScalarStyle.invalid: //default to plain
+ case ScalarStyle.plain:
+ put(writer, ":");
+ break;
+ }
+ if (value != "")
+ {
+ writer.formattedWrite!"%s"(value.substitute("\n", "\\n", `\`, `\\`, "\r", "\\r", "\t", "\\t", "\b", "\\b"));
+ }
+ break;
+ case EventID.streamStart:
+ put(writer, "+STR");
+ break;
+ case EventID.documentStart:
+ put(writer, "+DOC");
+ if (explicitDocument)
+ {
+ put(writer, " ---");
+ }
+ break;
+ case EventID.mappingStart:
+ put(writer, "+MAP");
+ if (collectionStyle == CollectionStyle.flow)
+ {
+ put(writer, " {}");
+ }
+ if (anchor != "")
+ {
+ put(writer, " &");
+ put(writer, anchor);
+ }
+ if (tag != "")
+ {
+ put(writer, " <");
+ put(writer, tag);
+ put(writer, ">");
+ }
+ break;
+ case EventID.sequenceStart:
+ put(writer, "+SEQ");
+ if (collectionStyle == CollectionStyle.flow)
+ {
+ put(writer, " []");
+ }
+ if (anchor != "")
+ {
+ put(writer, " &");
+ put(writer, anchor);
+ }
+ if (tag != "")
+ {
+ put(writer, " <");
+ put(writer, tag);
+ put(writer, ">");
+ }
+ break;
+ case EventID.streamEnd:
+ put(writer, "-STR");
+ break;
+ case EventID.documentEnd:
+ put(writer, "-DOC");
+ if (explicitDocument)
+ {
+ put(writer, " ...");
+ }
+ break;
+ case EventID.mappingEnd:
+ put(writer, "-MAP");
+ break;
+ case EventID.sequenceEnd:
+ put(writer, "-SEQ");
+ break;
+ case EventID.alias_:
+ put(writer, "=ALI *");
+ put(writer, anchor);
+ break;
+ case EventID.invalid:
+ assert(0, "Invalid EventID produced");
+ }
+ }
}
/**
diff --git a/src/ext_depends/D-YAML/source/dyaml/exception.d b/src/ext_depends/D-YAML/source/dyaml/exception.d
index 145e9c3..8a2fe0d 100644
--- a/src/ext_depends/D-YAML/source/dyaml/exception.d
+++ b/src/ext_depends/D-YAML/source/dyaml/exception.d
@@ -7,165 +7,201 @@
///Exceptions thrown by D:YAML and _exception related code.
module dyaml.exception;
-
import std.algorithm;
import std.array;
-import std.string;
import std.conv;
+import std.exception;
+import std.format;
+import std.range;
+import std.string;
+import std.typecons;
/// Base class for all exceptions thrown by D:YAML.
class YAMLException : Exception
{
- /// Construct a YAMLException with specified message and position where it was thrown.
- public this(string msg, string file = __FILE__, size_t line = __LINE__)
- @safe pure nothrow @nogc
- {
- super(msg, file, line);
- }
+ mixin basicExceptionCtors;
}
/// Position in a YAML stream, used for error messages.
struct Mark
{
- package:
- /// File name.
- string name_;
- /// Line number.
- ushort line_;
- /// Column number.
- ushort column_;
+ /// File name.
+ string name = "<unknown>";
+ /// Line number.
+ ushort line;
+ /// Column number.
+ ushort column;
public:
/// Construct a Mark with specified line and column in the file.
this(string name, const uint line, const uint column) @safe pure nothrow @nogc
{
- name_ = name;
- line_ = cast(ushort)min(ushort.max, line);
+ this.name = name;
+ this.line = cast(ushort)min(ushort.max, line);
// This *will* overflow on extremely wide files but saves CPU time
// (mark ctor takes ~5% of time)
- column_ = cast(ushort)column;
- }
-
- /// Get a file name.
- @property string name() @safe pure nothrow @nogc const
- {
- return name_;
- }
-
- /// Get a line number.
- @property ushort line() @safe pure nothrow @nogc const
- {
- return line_;
- }
-
- /// Get a column number.
- @property ushort column() @safe pure nothrow @nogc const
- {
- return column_;
- }
-
- /// Duplicate a mark
- Mark dup () const scope @safe pure nothrow
- {
- return Mark(this.name_.idup, this.line_, this.column_);
+ this.column = cast(ushort)column;
}
/// Get a string representation of the mark.
- string toString() const scope @safe pure nothrow
+ void toString(W)(ref W writer) const scope
{
// Line/column numbers start at zero internally, make them start at 1.
- static string clamped(ushort v) @safe pure nothrow
+ void writeClamped(ushort v)
{
- return text(v + 1, v == ushort.max ? " or higher" : "");
+ writer.formattedWrite!"%s"(v + 1);
+ if (v == ushort.max)
+ {
+ put(writer, "or higher");
+ }
}
- return "file " ~ name_ ~ ",line " ~ clamped(line_) ~ ",column " ~ clamped(column_);
+ put(writer, name);
+ put(writer, ":");
+ writeClamped(line);
+ put(writer, ",");
+ writeClamped(column);
}
}
-// Base class of YAML exceptions with marked positions of the problem.
+/// Base class of YAML exceptions with marked positions of the problem.
abstract class MarkedYAMLException : YAMLException
{
/// Position of the error.
Mark mark;
+ /// Additional position information, usually the start of a token or scalar
+ Nullable!Mark mark2;
+ /// A label for the extra information
+ string mark2Label;
- // Construct a MarkedYAMLException with specified context and problem.
- this(string context, scope const Mark contextMark,
- string problem, scope const Mark problemMark,
+ // Construct a MarkedYAMLException with two marks
+ this(string context, const Mark mark, string mark2Label, const Nullable!Mark mark2,
string file = __FILE__, size_t line = __LINE__) @safe pure nothrow
{
- const msg = context ~ '\n' ~
- (contextMark != problemMark ? contextMark.toString() ~ '\n' : "") ~
- problem ~ '\n' ~ problemMark.toString() ~ '\n';
- super(msg, file, line);
- mark = problemMark.dup;
+ super(context, file, line);
+ this.mark = mark;
+ this.mark2 = mark2;
+ this.mark2Label = mark2Label;
}
// Construct a MarkedYAMLException with specified problem.
- this(string problem, scope const Mark problemMark,
+ this(string msg, const Mark mark,
string file = __FILE__, size_t line = __LINE__)
@safe pure nothrow
{
- super(problem ~ '\n' ~ problemMark.toString(), file, line);
- mark = problemMark.dup;
+ super(msg, file, line);
+ this.mark = mark;
}
- /// Construct a MarkedYAMLException from a struct storing constructor parameters.
- this(ref const(MarkedYAMLExceptionData) data) @safe pure nothrow
+ /// Custom toString to add context without requiring allocation up-front
+ void toString(W)(ref W sink) const
+ {
+ sink.formattedWrite!"%s@%s(%s): "(typeid(this).name, file, line);
+ put(sink, msg);
+ put(sink, "\n");
+ mark.toString(sink);
+ if (!mark2.isNull)
+ {
+ put(sink, "\n");
+ put(sink, mark2Label);
+ put(sink, ":");
+ mark2.get.toString(sink);
+ }
+ put(sink, "\n");
+ put(sink, info.toString());
+ }
+ /// Ditto
+ override void toString(scope void delegate(in char[]) sink) const
{
- with(data) this(context, contextMark, problem, problemMark);
+ toString!(typeof(sink))(sink);
+ }
+ /// An override of message
+ override const(char)[] message() const @safe nothrow
+ {
+ if (mark2.isNull)
+ {
+ return assertNotThrown(text(msg, "\n", mark));
+ }
+ else
+ {
+ return assertNotThrown(text(msg, "\n", mark, "\n", mark2Label, ": ", mark2.get));
+ }
}
}
-package:
-// A struct storing parameters to the MarkedYAMLException constructor.
-struct MarkedYAMLExceptionData
+/// Exception thrown on composer errors.
+class ComposerException : MarkedYAMLException
{
- // Context of the error.
- string context;
- // Position of the context in a YAML buffer.
- Mark contextMark;
- // The error itself.
- string problem;
- // Position if the error.
- Mark problemMark;
+ mixin MarkedExceptionCtors;
}
-// Constructors of YAML exceptions are mostly the same, so we use a mixin.
-//
-// See_Also: YAMLException
-template ExceptionCtors()
+/// Exception thrown on constructor errors.
+class ConstructorException : MarkedYAMLException
{
- public this(string msg, string file = __FILE__, size_t line = __LINE__)
- @safe pure nothrow
- {
- super(msg, file, line);
- }
+ mixin MarkedExceptionCtors;
+}
+
+/// Exception thrown on loader errors.
+class LoaderException : MarkedYAMLException
+{
+ mixin MarkedExceptionCtors;
}
-// Constructors of marked YAML exceptions are mostly the same, so we use a mixin.
-//
-// See_Also: MarkedYAMLException
+/// Exception thrown on node related errors.
+class NodeException : MarkedYAMLException
+{
+ mixin MarkedExceptionCtors;
+}
+
+/// Exception thrown on parser errors.
+class ParserException : MarkedYAMLException
+{
+ mixin MarkedExceptionCtors;
+}
+
+/// Exception thrown on Reader errors.
+class ReaderException : MarkedYAMLException
+{
+ mixin MarkedExceptionCtors;
+}
+
+/// Exception thrown on Representer errors.
+class RepresenterException : YAMLException
+{
+ mixin basicExceptionCtors;
+}
+
+/// Exception thrown on scanner errors.
+class ScannerException : MarkedYAMLException
+{
+ mixin MarkedExceptionCtors;
+}
+
+private:
+
+/// Constructors of marked YAML exceptions are identical, so we use a mixin.
+///
+/// See_Also: MarkedYAMLException
template MarkedExceptionCtors()
{
public:
- this(string context, const Mark contextMark, string problem,
- const Mark problemMark, string file = __FILE__, size_t line = __LINE__)
+ this(string msg, const Mark mark1, string mark2Label,
+ const Mark mark2, string file = __FILE__, size_t line = __LINE__)
@safe pure nothrow
{
- super(context, contextMark, problem, problemMark,
- file, line);
+ super(msg, mark1, mark2Label, Nullable!Mark(mark2), file, line);
}
- this(string problem, const Mark problemMark,
+ this(string msg, const Mark mark,
string file = __FILE__, size_t line = __LINE__)
@safe pure nothrow
{
- super(problem, problemMark, file, line);
+ super(msg, mark, file, line);
}
-
- this(ref const(MarkedYAMLExceptionData) data) @safe pure nothrow
+ this(string msg, const Mark mark1, string mark2Label,
+ const Nullable!Mark mark2, string file = __FILE__, size_t line = __LINE__)
+ @safe pure nothrow
{
- super(data);
+ super(msg, mark1, mark2Label, mark2, file, line);
}
}
diff --git a/src/ext_depends/D-YAML/source/dyaml/loader.d b/src/ext_depends/D-YAML/source/dyaml/loader.d
index 6638dfc..358855a 100644
--- a/src/ext_depends/D-YAML/source/dyaml/loader.d
+++ b/src/ext_depends/D-YAML/source/dyaml/loader.d
@@ -33,14 +33,8 @@ import dyaml.token;
struct Loader
{
private:
- // Processes character data to YAML tokens.
- Scanner scanner_;
- // Processes tokens to YAML events.
- Parser parser_;
- // Resolves tags (data types).
- Resolver resolver_;
- // Name of the input file or stream, used in error messages.
- string name_ = "<unknown>";
+ // Assembles YAML documents
+ Composer composer_;
// Are we done loading?
bool done_;
// Last node read from stream
@@ -49,10 +43,6 @@ struct Loader
bool rangeInitialized;
public:
- @disable this();
- @disable int opCmp(ref Loader);
- @disable bool opEquals(ref Loader);
-
/** Construct a Loader to load YAML from a file.
*
* Params: filename = Name of the file to load from.
@@ -147,33 +137,30 @@ struct Loader
/// Ditto
private this(ubyte[] yamlData, string name = "<unknown>") @safe
{
- resolver_ = Resolver.withDefaultResolvers;
- name_ = name;
try
{
- auto reader_ = new Reader(yamlData, name);
- scanner_ = Scanner(reader_);
- parser_ = new Parser(scanner_);
+ auto reader = Reader(yamlData, name);
+ auto parser = new Parser(Scanner(reader));
+ composer_ = Composer(parser, Resolver.withDefaultResolvers);
}
- catch(YAMLException e)
+ catch(MarkedYAMLException e)
{
- throw new YAMLException("Unable to open %s for YAML loading: %s"
- .format(name_, e.msg), e.file, e.line);
+ throw new LoaderException("Unable to open %s for YAML loading: %s"
+ .format(name, e.msg), e.mark, e.file, e.line);
}
}
/// Set stream _name. Used in debugging messages.
- void name(string name) pure @safe nothrow @nogc
+ ref inout(string) name() inout @safe return pure nothrow @nogc
{
- name_ = name;
- scanner_.name = name;
+ return composer_.name;
}
/// Specify custom Resolver to use.
auto ref resolver() pure @safe nothrow @nogc
{
- return resolver_;
+ return composer_.resolver;
}
/** Load single YAML document.
@@ -189,10 +176,12 @@ struct Loader
*/
Node load() @safe
{
- enforce!YAMLException(!empty, "Zero documents in stream");
+ enforce(!empty,
+ new LoaderException("Zero documents in stream", composer_.mark));
auto output = front;
popFront();
- enforce!YAMLException(empty, "More than one document in stream");
+ enforce(empty,
+ new LoaderException("More than one document in stream", composer_.mark));
return output;
}
@@ -217,22 +206,23 @@ struct Loader
*/
void popFront() @safe
{
- // Composer initialization is done here in case the constructor is
- // modified, which is a pretty common case.
- static Composer composer;
- if (!rangeInitialized)
- {
- composer = Composer(parser_, resolver_);
- rangeInitialized = true;
- }
+ scope(success) rangeInitialized = true;
assert(!done_, "Loader.popFront called on empty range");
- if (composer.checkNode())
+ try
{
- currentNode = composer.getNode();
+ if (composer_.checkNode())
+ {
+ currentNode = composer_.getNode();
+ }
+ else
+ {
+ done_ = true;
+ }
}
- else
+ catch(MarkedYAMLException e)
{
- done_ = true;
+ throw new LoaderException("Unable to load %s: %s"
+ .format(name, e.msg), e.mark, e.mark2Label, e.mark2, e.file, e.line);
}
}
/** Implements the front range primitive.
@@ -248,30 +238,6 @@ struct Loader
}
return currentNode;
}
-
- // Scan all tokens, throwing them away. Used for benchmarking.
- void scanBench() @safe
- {
- try
- {
- while(!scanner_.empty)
- {
- scanner_.popFront();
- }
- }
- catch(YAMLException e)
- {
- throw new YAMLException("Unable to scan YAML from stream " ~
- name_ ~ " : " ~ e.msg, e.file, e.line);
- }
- }
-
-
- // Parse and return all events. Used for debugging.
- auto parse() @safe
- {
- return parser_;
- }
}
/// Load single YAML document from a file:
@safe unittest
@@ -408,6 +374,89 @@ EOS";
loader.name = filename;
Node unused;
- auto e = loader.load().collectException!ScannerException(unused);
+ auto e = loader.load().collectException!LoaderException(unused);
assert(e.mark.name == filename);
}
+/// https://github.com/dlang-community/D-YAML/issues/325
+@safe unittest
+{
+ assert(Loader.fromString("--- {x: a}").load()["x"] == "a");
+}
+
+// Ensure exceptions are thrown as appropriate
+@safe unittest
+{
+ LoaderException e;
+ // No documents
+ e = collectException!LoaderException(Loader.fromString("", "filename.yaml").load());
+ assert(e);
+ with(e)
+ {
+ assert(mark.name == "filename.yaml");
+ assert(mark.line == 0);
+ assert(mark.column == 0);
+ }
+ // Too many documents
+ e = collectException!LoaderException(Loader.fromString("--- 4\n--- 6\n--- 5", "filename.yaml").load());
+ assert(e, "No exception thrown");
+ with(e)
+ {
+ assert(mark.name == "filename.yaml");
+ // FIXME: should be position of second document, not end of file
+ //assert(mark.line == 1);
+ //assert(mark.column == 0);
+ }
+ // Invalid document
+ e = collectException!LoaderException(Loader.fromString("[", "filename.yaml").load());
+ assert(e, "No exception thrown");
+ with(e)
+ {
+ assert(mark.name == "filename.yaml");
+ // FIXME: should be position of second document, not end of file
+ assert(mark.line == 0);
+ assert(mark.column == 1);
+ }
+}
+
+@safe unittest
+{
+ assertThrown(Loader.fromString("Invalid character: \xFF").load());
+}
+
+@safe unittest
+{
+ assertThrown(Loader.fromFile("test/data/odd-utf16.stream-error").load());
+}
+
+// UTF-16 and 32 test
+@safe unittest
+{
+ import std.conv : to;
+ import std.range : only;
+ enum string yaml = `ABCDØ`;
+ enum bom = '\uFEFF';
+ foreach (doc; only(
+ cast(ubyte[])(bom~yaml.to!(wchar[])),
+ cast(ubyte[])(bom~yaml.to!(dchar[])),
+ ))
+ {
+ assert(Loader.fromBuffer(doc).load().as!string == yaml);
+ }
+}
+// Invalid unicode test
+@safe unittest
+{
+ import std.conv : to;
+ import std.range : only;
+ enum string yaml = `ABCDØ`;
+ enum badBOM = '\uFFFE';
+ foreach (doc; only(
+ cast(ubyte[])yaml.to!(wchar[]),
+ cast(ubyte[])yaml.to!(dchar[]),
+ cast(ubyte[])(badBOM~yaml.to!(wchar[])),
+ cast(ubyte[])(badBOM~yaml.to!(dchar[])),
+ ))
+ {
+ assertThrown(Loader.fromBuffer(doc).load());
+ }
+}
diff --git a/src/ext_depends/D-YAML/source/dyaml/node.d b/src/ext_depends/D-YAML/source/dyaml/node.d
index 4c3c5eb..fd47f7e 100644
--- a/src/ext_depends/D-YAML/source/dyaml/node.d
+++ b/src/ext_depends/D-YAML/source/dyaml/node.d
@@ -30,22 +30,6 @@ import dyaml.event;
import dyaml.exception;
import dyaml.style;
-/// Exception thrown at node related errors.
-class NodeException : MarkedYAMLException
-{
- package:
- // Construct a NodeException.
- //
- // Params: msg = Error message.
- // start = Start position of the node.
- this(string msg, const scope Mark start,
- string file = __FILE__, size_t line = __LINE__)
- @safe pure nothrow
- {
- super(msg, start, file, line);
- }
-}
-
// Node kinds.
enum NodeID : ubyte
{
diff --git a/src/ext_depends/D-YAML/source/dyaml/parser.d b/src/ext_depends/D-YAML/source/dyaml/parser.d
index befdfa4..cc2ea47 100644
--- a/src/ext_depends/D-YAML/source/dyaml/parser.d
+++ b/src/ext_depends/D-YAML/source/dyaml/parser.d
@@ -88,16 +88,6 @@ import dyaml.tagdirective;
*/
-/**
- * Marked exception thrown at parser errors.
- *
- * See_Also: MarkedYAMLException
- */
-class ParserException : MarkedYAMLException
-{
- mixin MarkedExceptionCtors;
-}
-
package:
/// Generates events from tokens provided by a Scanner.
///
@@ -173,6 +163,17 @@ final class Parser
ensureState();
}
+ /// Set file name.
+ ref inout(string) name() inout @safe return pure nothrow @nogc
+ {
+ return scanner_.name;
+ }
+ /// Get a mark from the current reader position
+ Mark mark() const @safe pure nothrow @nogc
+ {
+ return scanner_.mark;
+ }
+
private:
/// If current event is invalid, load the next valid one if possible.
void ensureState() @safe
@@ -508,9 +509,9 @@ final class Parser
}
const token = scanner_.front;
- throw new ParserException("While parsing a " ~ (block ? "block" : "flow") ~ " node",
- startMark, "expected node content, but found: "
- ~ token.idString, token.startMark);
+ throw new ParserException("While parsing a " ~ (block ? "block" : "flow")
+ ~ " node, expected node content, but found: " ~ token.idString,
+ token.startMark, "node started here", startMark);
}
/// Handle escape sequences in a double quoted scalar.
@@ -618,8 +619,8 @@ final class Parser
}
//handle must be in tagDirectives_
enforce(replacement !is null,
- new ParserException("While parsing a node", startMark,
- "found undefined tag handle: " ~ handle, tagMark));
+ new ParserException("While parsing a node, found undefined tag handle: "
+ ~ handle, tagMark, "node started here", startMark));
return replacement ~ suffix;
}
return suffix;
@@ -658,9 +659,8 @@ final class Parser
if(scanner_.front.id != TokenID.blockEnd)
{
const token = scanner_.front;
- throw new ParserException("While parsing a block collection", marks_.data.back,
- "expected block end, but found " ~ token.idString,
- token.startMark);
+ throw new ParserException("While parsing a block sequence, expected block end, but found: "
+ ~ token.idString, token.startMark, "sequence started here", marks_.data.back);
}
state_ = popState();
@@ -730,9 +730,8 @@ final class Parser
if(scanner_.front.id != TokenID.blockEnd)
{
const token = scanner_.front;
- throw new ParserException("While parsing a block mapping", marks_.data.back,
- "expected block end, but found: " ~ token.idString,
- token.startMark);
+ throw new ParserException("While parsing a block mapping, expected block end, but found: "
+ ~ token.idString, token.startMark, "mapping started here", marks_.data.back);
}
state_ = popState();
@@ -797,9 +796,8 @@ final class Parser
else
{
const token = scanner_.front;
- throw new ParserException("While parsing a flow sequence", marks_.data.back,
- "expected ',' or ']', but got: " ~
- token.idString, token.startMark);
+ throw new ParserException("While parsing a flow sequence, expected ',' or ']', but got: " ~
+ token.idString, token.startMark, "sequence started here", marks_.data.back);
}
}
@@ -912,9 +910,8 @@ final class Parser
else
{
const token = scanner_.front;
- throw new ParserException("While parsing a flow mapping", marks_.data.back,
- "expected ',' or '}', but got: " ~
- token.idString, token.startMark);
+ throw new ParserException("While parsing a flow mapping, expected ',' or '}', but got: "
+ ~ token.idString, token.startMark, "mapping started here", marks_.data.back);
}
}
diff --git a/src/ext_depends/D-YAML/source/dyaml/queue.d b/src/ext_depends/D-YAML/source/dyaml/queue.d
index 57b0d34..35b45c8 100644
--- a/src/ext_depends/D-YAML/source/dyaml/queue.d
+++ b/src/ext_depends/D-YAML/source/dyaml/queue.d
@@ -88,11 +88,6 @@ private:
}
public:
-
- @disable void opAssign(ref Queue);
- @disable bool opEquals(ref Queue);
- @disable int opCmp(ref Queue);
-
this(this) @safe nothrow @nogc
{
auto node = first_;
diff --git a/src/ext_depends/D-YAML/source/dyaml/reader.d b/src/ext_depends/D-YAML/source/dyaml/reader.d
index ae44c80..824c1d1 100644
--- a/src/ext_depends/D-YAML/source/dyaml/reader.d
+++ b/src/ext_depends/D-YAML/source/dyaml/reader.d
@@ -31,19 +31,8 @@ alias isBreak = among!('\n', '\u0085', '\u2028', '\u2029');
package:
-///Exception thrown at Reader errors.
-class ReaderException : YAMLException
-{
- this(string msg, string file = __FILE__, size_t line = __LINE__)
- @safe pure nothrow
- {
- super("Reader error: " ~ msg, file, line);
- }
-}
-
-/// Provides an API to read characters from a UTF-8 buffer and build slices into that
-/// buffer to avoid allocations (see SliceBuilder).
-final class Reader
+/// Provides an API to read characters from a UTF-8 buffer.
+struct Reader
{
private:
// Buffer of currently loaded characters.
@@ -102,8 +91,9 @@ final class Reader
auto endianResult = fixUTFByteOrder(buffer);
if(endianResult.bytesStripped > 0)
{
+ // TODO: add line and column
throw new ReaderException("Size of UTF-16 or UTF-32 input not aligned " ~
- "to 2 or 4 bytes, respectively");
+ "to 2 or 4 bytes, respectively", Mark(name, 0, 0));
}
version(unittest) { endian_ = endianResult.endian; }
@@ -113,17 +103,18 @@ final class Reader
const msg = utf8Result.errorMessage;
if(msg !is null)
{
- throw new ReaderException("Error when converting to UTF-8: " ~ msg);
+ // TODO: add line and column
+ throw new ReaderException("Error when converting to UTF-8: " ~ msg, Mark(name, 0, 0));
}
buffer_ = utf8Result.utf8;
characterCount_ = utf8Result.characterCount;
// Check that all characters in buffer are printable.
+ // TODO: add line and column
enforce(isPrintableValidUTF8(buffer_),
- new ReaderException("Special unicode characters are not allowed"));
+ new ReaderException("Special unicode characters are not allowed", Mark(name, 0, 0)));
- this.sliceBuilder = SliceBuilder(this);
checkASCII();
}
@@ -212,8 +203,7 @@ final class Reader
/// Get specified number of characters starting at current position.
///
/// Note: This gets only a "view" into the internal buffer, which will be
- /// invalidated after other Reader calls. Use SliceBuilder to build slices
- /// for permanent use.
+ /// invalidated after other Reader calls.
///
/// Params: length = Number of characters (code points, not bytes) to get. May
/// reach past the end of the buffer; in that case the returned
@@ -228,8 +218,7 @@ final class Reader
/// Get specified number of bytes, not code points, starting at current position.
///
/// Note: This gets only a "view" into the internal buffer, which will be
- /// invalidated after other Reader calls. Use SliceBuilder to build slices
- /// for permanent use.
+ /// invalidated after other Reader calls.
///
/// Params: length = Number bytes (not code points) to get. May NOT reach past
/// the end of the buffer; should be used with peek() to avoid
@@ -396,17 +385,34 @@ final class Reader
checkASCII();
}
- /// Used to build slices of read data in Reader; to avoid allocations.
- SliceBuilder sliceBuilder;
-
- /// Get a string describing current buffer position, used for error messages.
+ /// Get filename, line and column of current position.
Mark mark() const pure nothrow @nogc @safe { return Mark(name_, line_, column_); }
- /// Get file name.
- string name() const @safe pure nothrow @nogc { return name_; }
+ /// Get filename, line and column of current position + some number of chars
+ Mark mark(size_t advance) const pure @safe
+ {
+ auto lineTemp = cast()line_;
+ auto columnTemp = cast()column_;
+ auto bufferOffsetTemp = cast()bufferOffset_;
+ for (size_t pos = 0; pos < advance; pos++)
+ {
+ if (bufferOffsetTemp >= buffer_.length)
+ {
+ break;
+ }
+ const c = decode(buffer_, bufferOffsetTemp);
+ if (c.isBreak || (c == '\r' && buffer_[bufferOffsetTemp] == '\n'))
+ {
+ lineTemp++;
+ columnTemp = 0;
+ }
+ columnTemp++;
+ }
+ return Mark(name_, lineTemp, columnTemp);
+ }
- /// Set file name.
- void name(string name) pure @safe nothrow @nogc { name_ = name; }
+ /// Get file name.
+ ref inout(string) name() inout @safe return pure nothrow @nogc { return name_; }
/// Get current line number.
uint line() const @safe pure nothrow @nogc { return line_; }
@@ -448,267 +454,6 @@ private:
}
}
-/// Used to build slices of already read data in Reader buffer, avoiding allocations.
-///
-/// Usually these slices point to unchanged Reader data, but sometimes the data is
-/// changed due to how YAML interprets certain characters/strings.
-///
-/// See begin() documentation.
-struct SliceBuilder
-{
-private:
- // No copying by the user.
- @disable this(this);
- @disable void opAssign(ref SliceBuilder);
-
- // Reader this builder works in.
- Reader reader_;
-
- // Start of the slice om reader_.buffer_ (size_t.max while no slice being build)
- size_t start_ = size_t.max;
- // End of the slice om reader_.buffer_ (size_t.max while no slice being build)
- size_t end_ = size_t.max;
-
- // Stack of slice ends to revert to (see Transaction)
- //
- // Very few levels as we don't want arbitrarily nested transactions.
- size_t[4] endStack_;
- // The number of elements currently in endStack_.
- size_t endStackUsed_;
-
- @safe const pure nothrow @nogc invariant()
- {
- if(!inProgress) { return; }
- assert(end_ <= reader_.bufferOffset_, "Slice ends after buffer position");
- assert(start_ <= end_, "Slice start after slice end");
- }
-
- // Is a slice currently being built?
- bool inProgress() @safe const pure nothrow @nogc
- in(start_ == size_t.max ? end_ == size_t.max : end_ != size_t.max, "start_/end_ are not consistent")
- {
- return start_ != size_t.max;
- }
-
-public:
- /// Begin building a slice.
- ///
- /// Only one slice can be built at any given time; before beginning a new slice,
- /// finish the previous one (if any).
- ///
- /// The slice starts at the current position in the Reader buffer. It can only be
- /// extended up to the current position in the buffer; Reader methods get() and
- /// forward() move the position. E.g. it is valid to extend a slice by write()-ing
- /// a string just returned by get() - but not one returned by prefix() unless the
- /// position has changed since the prefix() call.
- void begin() @safe pure nothrow @nogc
- in(!inProgress, "Beginning a slice while another slice is being built")
- in(endStackUsed_ == 0, "Slice stack not empty at slice begin")
- {
-
- start_ = reader_.bufferOffset_;
- end_ = reader_.bufferOffset_;
- }
-
- /// Finish building a slice and return it.
- ///
- /// Any Transactions on the slice must be committed or destroyed before the slice
- /// is finished.
- ///
- /// Returns a string; once a slice is finished it is definitive that its contents
- /// will not be changed.
- char[] finish() @safe pure nothrow @nogc
- in(inProgress, "finish called without begin")
- in(endStackUsed_ == 0, "Finishing a slice with running transactions.")
- {
-
- auto result = reader_.buffer_[start_ .. end_];
- start_ = end_ = size_t.max;
- return result;
- }
-
- /// Write a string to the slice being built.
- ///
- /// Data can only be written up to the current position in the Reader buffer.
- ///
- /// If str is a string returned by a Reader method, and str starts right after the
- /// end of the slice being built, the slice is extended (trivial operation).
- ///
- /// See_Also: begin
- void write(scope char[] str) @safe pure nothrow @nogc
- {
- assert(inProgress, "write called without begin");
- assert(end_ <= reader_.bufferOffset_,
- "AT START: Slice ends after buffer position");
-
- // Nothing? Already done.
- if (str.length == 0) { return; }
- // If str starts at the end of the slice (is a string returned by a Reader
- // method), just extend the slice to contain str.
- if(&str[0] == &reader_.buffer_[end_])
- {
- end_ += str.length;
- }
- // Even if str does not start at the end of the slice, it still may be returned
- // by a Reader method and point to buffer. So we need to memmove.
- else
- {
- copy(str, reader_.buffer_[end_..end_ + str.length * char.sizeof]);
- end_ += str.length;
- }
- }
-
- /// Write a character to the slice being built.
- ///
- /// Data can only be written up to the current position in the Reader buffer.
- ///
- /// See_Also: begin
- void write(dchar c) @safe pure
- in(inProgress, "write called without begin")
- {
- if(c < 0x80)
- {
- reader_.buffer_[end_++] = cast(char)c;
- return;
- }
-
- // We need to encode a non-ASCII dchar into UTF-8
- char[4] encodeBuf;
- const bytes = encode(encodeBuf, c);
- reader_.buffer_[end_ .. end_ + bytes] = encodeBuf[0 .. bytes];
- end_ += bytes;
- }
-
- /// Insert a character to a specified position in the slice.
- ///
- /// Enlarges the slice by 1 char. Note that the slice can only extend up to the
- /// current position in the Reader buffer.
- ///
- /// Params:
- ///
- /// c = The character to insert.
- /// position = Position to insert the character at in code units, not code points.
- /// Must be less than slice length(); a previously returned length()
- /// can be used.
- void insert(const dchar c, const size_t position) @safe pure
- in(inProgress, "insert called without begin")
- in(start_ + position <= end_, "Trying to insert after the end of the slice")
- {
-
- const point = start_ + position;
- const movedLength = end_ - point;
-
- // Encode c into UTF-8
- char[4] encodeBuf;
- if(c < 0x80) { encodeBuf[0] = cast(char)c; }
- const size_t bytes = c < 0x80 ? 1 : encode(encodeBuf, c);
-
- if(movedLength > 0)
- {
- copy(reader_.buffer_[point..point + movedLength * char.sizeof],
- reader_.buffer_[point + bytes..point + bytes + movedLength * char.sizeof]);
- }
- reader_.buffer_[point .. point + bytes] = encodeBuf[0 .. bytes];
- end_ += bytes;
- }
-
- /// Get the current length of the slice.
- size_t length() @safe const pure nothrow @nogc
- {
- return end_ - start_;
- }
-
- /// A slice building transaction.
- ///
- /// Can be used to save and revert back to slice state.
- struct Transaction
- {
- private:
- // The slice builder affected by the transaction.
- SliceBuilder* builder_;
- // Index of the return point of the transaction in StringBuilder.endStack_.
- size_t stackLevel_;
- // True after commit() has been called.
- bool committed_;
-
- public:
- /// Begins a transaction on a SliceBuilder object.
- ///
- /// The transaction must end $(B after) any transactions created within the
- /// transaction but $(B before) the slice is finish()-ed. A transaction can be
- /// ended either by commit()-ing or reverting through the destructor.
- ///
- /// Saves the current state of a slice.
- this(SliceBuilder* builder) @safe pure nothrow @nogc
- {
- builder_ = builder;
- stackLevel_ = builder_.endStackUsed_;
- builder_.push();
- }
-
- /// Commit changes to the slice.
- ///
- /// Ends the transaction - can only be called once, and removes the possibility
- /// to revert slice state.
- ///
- /// Does nothing for a default-initialized transaction (the transaction has not
- /// been started yet).
- void commit() @safe pure nothrow @nogc
- in(!committed_, "Can't commit a transaction more than once")
- {
-
- if(builder_ is null) { return; }
- assert(builder_.endStackUsed_ == stackLevel_ + 1,
- "Parent transactions don't fully contain child transactions");
- builder_.apply();
- committed_ = true;
- }
-
- /// Destroy the transaction and revert it if it hasn't been committed yet.
- void end() @safe pure nothrow @nogc
- in(builder_ && builder_.endStackUsed_ == stackLevel_ + 1, "Parent transactions don't fully contain child transactions")
- {
- builder_.pop();
- builder_ = null;
- }
-
- }
-
-private:
- // Push the current end of the slice so we can revert to it if needed.
- //
- // Used by Transaction.
- void push() @safe pure nothrow @nogc
- in(inProgress, "push called without begin")
- in(endStackUsed_ < endStack_.length, "Slice stack overflow")
- {
- endStack_[endStackUsed_++] = end_;
- }
-
- // Pop the current end of endStack_ and set the end of the slice to the popped
- // value, reverting changes since the old end was pushed.
- //
- // Used by Transaction.
- void pop() @safe pure nothrow @nogc
- in(inProgress, "pop called without begin")
- in(endStackUsed_ > 0, "Trying to pop an empty slice stack")
- {
- end_ = endStack_[--endStackUsed_];
- }
-
- // Pop the current end of endStack_, but keep the current end of the slice, applying
- // changes made since pushing the old end.
- //
- // Used by Transaction.
- void apply() @safe pure nothrow @nogc
- in(inProgress, "apply called without begin")
- in(endStackUsed_ > 0, "Trying to apply an empty slice stack")
- {
- --endStackUsed_;
- }
-}
-
-
private:
// Convert a UTF-8/16/32 buffer to UTF-8, in-place if possible.
@@ -728,7 +473,7 @@ private:
// this first.
// $(D char[] utf8) input converted to UTF-8. May be a slice of input.
// $(D size_t characterCount) Number of characters (code points) in input.
-auto toUTF8(ubyte[] input, const UTFEncoding encoding) @safe pure nothrow
+public auto toUTF8(ubyte[] input, const UTFEncoding encoding) @safe pure nothrow
{
// Documented in function ddoc.
struct Result
diff --git a/src/ext_depends/D-YAML/source/dyaml/representer.d b/src/ext_depends/D-YAML/source/dyaml/representer.d
index f903b60..4d36ec6 100644
--- a/src/ext_depends/D-YAML/source/dyaml/representer.d
+++ b/src/ext_depends/D-YAML/source/dyaml/representer.d
@@ -31,11 +31,6 @@ import dyaml.serializer;
import dyaml.style;
package:
-///Exception thrown on Representer errors.
-class RepresenterException : YAMLException
-{
- mixin ExceptionCtors;
-}
/**
* Represents YAML nodes as scalar, sequence and mapping nodes ready for output.
diff --git a/src/ext_depends/D-YAML/source/dyaml/resolver.d b/src/ext_depends/D-YAML/source/dyaml/resolver.d
index 16d8419..4e82931 100644
--- a/src/ext_depends/D-YAML/source/dyaml/resolver.d
+++ b/src/ext_depends/D-YAML/source/dyaml/resolver.d
@@ -100,9 +100,6 @@ struct Resolver
}
public:
- @disable bool opEquals(ref Resolver);
- @disable int opCmp(ref Resolver);
-
/**
* Add an implicit scalar resolver.
*
@@ -209,45 +206,6 @@ struct Resolver
assert(false, "Cannot resolve an invalid node");
}
}
- @safe unittest
- {
- auto resolver = Resolver.withDefaultResolvers;
-
- bool tagMatch(string tag, string[] values) @safe
- {
- const string expected = tag;
- foreach(value; values)
- {
- const string resolved = resolver.resolve(NodeID.scalar, null, value, true);
- if(expected != resolved)
- {
- return false;
- }
- }
- return true;
- }
-
- assert(tagMatch("tag:yaml.org,2002:bool",
- ["yes", "NO", "True", "on"]));
- assert(tagMatch("tag:yaml.org,2002:float",
- ["6.8523015e+5", "685.230_15e+03", "685_230.15",
- "190:20:30.15", "-.inf", ".NaN"]));
- assert(tagMatch("tag:yaml.org,2002:int",
- ["685230", "+685_230", "02472256", "0x_0A_74_AE",
- "0b1010_0111_0100_1010_1110", "190:20:30"]));
- assert(tagMatch("tag:yaml.org,2002:merge", ["<<"]));
- assert(tagMatch("tag:yaml.org,2002:null", ["~", "null", ""]));
- assert(tagMatch("tag:yaml.org,2002:str",
- ["abcd", "9a8b", "9.1adsf"]));
- assert(tagMatch("tag:yaml.org,2002:timestamp",
- ["2001-12-15T02:59:43.1Z",
- "2001-12-14t21:59:43.10-05:00",
- "2001-12-14 21:59:43.10 -5",
- "2001-12-15 2:59:43.10",
- "2002-12-14"]));
- assert(tagMatch("tag:yaml.org,2002:value", ["="]));
- assert(tagMatch("tag:yaml.org,2002:yaml", ["!", "&", "*"]));
- }
///Returns: Default scalar tag.
@property string defaultScalarTag() const pure @safe nothrow {return defaultScalarTag_;}
diff --git a/src/ext_depends/D-YAML/source/dyaml/scanner.d b/src/ext_depends/D-YAML/source/dyaml/scanner.d
index 77c3e38..f30ce79 100644
--- a/src/ext_depends/D-YAML/source/dyaml/scanner.d
+++ b/src/ext_depends/D-YAML/source/dyaml/scanner.d
@@ -74,14 +74,6 @@ alias isFlowScalarBreakSpace = among!(' ', '\t', '\0', '\n', '\r', '\u0085', '\u
alias isNSAnchorName = c => !c.isWhiteSpace && !c.among!('[', ']', '{', '}', ',', '\uFEFF');
-/// Marked exception thrown at scanner errors.
-///
-/// See_Also: MarkedYAMLException
-class ScannerException : MarkedYAMLException
-{
- mixin MarkedExceptionCtors;
-}
-
/// Generates tokens from data provided by a Reader.
struct Scanner
{
@@ -95,18 +87,12 @@ struct Scanner
/// We emit the KEY token before all keys, so when we find a potential simple
/// key, we try to locate the corresponding ':' indicator. Simple keys should be
/// limited to a single line and 1024 characters.
- ///
- /// 16 bytes on 64-bit.
static struct SimpleKey
{
- /// Character index in reader where the key starts.
- uint charIndex = uint.max;
+ /// Position of the key
+ Mark mark;
/// Index of the key token from start (first token scanned being 0).
uint tokenIndex;
- /// Line the key starts at.
- uint line;
- /// Column the key starts at.
- ushort column;
/// Is this required to be a simple key?
bool required;
/// Is this struct "null" (invalid)?.
@@ -188,9 +174,14 @@ struct Scanner
}
/// Set file name.
- void name(string name) @safe pure nothrow @nogc
+ ref inout(string) name() inout @safe return pure nothrow @nogc
{
- reader_.name = name;
+ return reader_.name;
+ }
+ /// Get a mark from the current reader position
+ Mark mark() const @safe pure nothrow @nogc
+ {
+ return reader_.mark;
}
private:
@@ -198,7 +189,7 @@ struct Scanner
/// function.
string expected(T)(string expected, T found)
{
- return text("expected ", expected, ", but found ", found);
+ return text(expected, ", but found ", found);
}
/// Determine whether or not we need to fetch more tokens before peeking/getting a token.
@@ -284,12 +275,11 @@ struct Scanner
foreach(level, ref key; possibleSimpleKeys_)
{
if(key.isNull) { continue; }
- if(key.line != reader_.line || reader_.charIndex - key.charIndex > 1024)
+ if(key.mark.line != reader_.mark.line || reader_.mark.column - key.mark.column > 1024)
{
enforce(!key.required,
- new ScannerException("While scanning a simple key",
- Mark(reader_.name, key.line, key.column),
- "could not find expected ':'", reader_.mark));
+ new ScannerException("While scanning a simple key, could not find expected ':'",
+ reader_.mark, "key started here", key.mark));
key.isNull = true;
}
}
@@ -311,17 +301,19 @@ struct Scanner
removePossibleSimpleKey();
const tokenCount = tokensTaken_ + cast(uint)tokens_.length;
- const line = reader_.line;
+ const line = reader_.line;
const column = reader_.column;
- const key = SimpleKey(cast(uint)reader_.charIndex, tokenCount, line,
- cast(ushort)min(column, ushort.max), required);
+ const key = SimpleKey(reader_.mark, tokenCount, required);
if(possibleSimpleKeys_.length <= flowLevel_)
{
const oldLength = possibleSimpleKeys_.length;
possibleSimpleKeys_.length = flowLevel_ + 1;
- //No need to initialize the last element, it's already done in the next line.
- possibleSimpleKeys_[oldLength .. flowLevel_] = SimpleKey.init;
+ // Make sure all the empty keys are null
+ foreach (ref emptyKey; possibleSimpleKeys_[oldLength .. flowLevel_])
+ {
+ emptyKey.isNull = true;
+ }
}
possibleSimpleKeys_[flowLevel_] = key;
}
@@ -335,9 +327,8 @@ struct Scanner
{
const key = possibleSimpleKeys_[flowLevel_];
enforce(!key.required,
- new ScannerException("While scanning a simple key",
- Mark(reader_.name, key.line, key.column),
- "could not find expected ':'", reader_.mark));
+ new ScannerException("While scanning a simple key, could not find expected ':'",
+ reader_.mark, "key started here", key.mark));
possibleSimpleKeys_[flowLevel_].isNull = true;
}
}
@@ -547,18 +538,18 @@ struct Scanner
!possibleSimpleKeys_[flowLevel_].isNull)
{
const key = possibleSimpleKeys_[flowLevel_];
+ assert(key.tokenIndex >= tokensTaken_);
+
possibleSimpleKeys_[flowLevel_].isNull = true;
- Mark keyMark = Mark(reader_.name, key.line, key.column);
+ Mark keyMark = key.mark;
const idx = key.tokenIndex - tokensTaken_;
- assert(idx >= 0);
-
// Add KEY.
// Manually inserting since tokens are immutable (need linked list).
tokens_.insert(keyToken(keyMark, keyMark), idx);
// If this key starts a new block mapping, we need to add BLOCK-MAPPING-START.
- if(flowLevel_ == 0 && addIndent(key.column))
+ if(flowLevel_ == 0 && addIndent(key.mark.column))
{
tokens_.insert(blockMappingStartToken(keyMark, keyMark), idx);
}
@@ -753,23 +744,23 @@ struct Scanner
///
/// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice.
- void scanAlphaNumericToSlice(string name)(const Mark startMark)
+ void scanAlphaNumericToSlice(string name)(ref char[] slice, const Mark startMark)
{
size_t length;
dchar c = reader_.peek();
while(c.isAlphaNum || c.among!('-', '_')) { c = reader_.peek(++length); }
- enforce(length > 0, new ScannerException("While scanning " ~ name,
- startMark, expected("alphanumeric, '-' or '_'", c), reader_.mark));
+ enforce(length > 0, new ScannerException(expected("While scanning a " ~ name ~ ", expected alphanumeric, '-' or '_'", c),
+ reader_.mark, name~" started here", startMark));
- reader_.sliceBuilder.write(reader_.get(length));
+ slice ~= reader_.get(length);
}
/// Scan a string.
///
/// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice.
- void scanAnchorAliasToSlice(const Mark startMark) @safe
+ char[] readAnchorAlias(const Mark startMark) @safe
{
size_t length;
dchar c = reader_.peek();
@@ -778,10 +769,11 @@ struct Scanner
c = reader_.peek(++length);
}
- enforce(length > 0, new ScannerException("While scanning an anchor or alias",
- startMark, expected("a printable character besides '[', ']', '{', '}' and ','", c), reader_.mark));
+ enforce(length > 0, new ScannerException(
+ expected("While scanning an anchor or alias, expected a printable character besides '[', ']', '{', '}' and ','", c),
+ reader_.mark, "started here", startMark));
- reader_.sliceBuilder.write(reader_.get(length));
+ return reader_.get(length);
}
/// Scan and throw away all characters until next line break.
@@ -794,14 +786,14 @@ struct Scanner
///
/// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice.
- void scanToNextBreakToSlice() @safe
+ void scanToNextBreakToSlice(ref char[] slice) @safe
{
uint length;
while(!reader_.peek(length).isBreak)
{
++length;
}
- reader_.sliceBuilder.write(reader_.get(length));
+ slice ~= reader_.get(length);
}
@@ -859,17 +851,15 @@ struct Scanner
reader_.forward();
// Scan directive name
- reader_.sliceBuilder.begin();
- scanDirectiveNameToSlice(startMark);
- const name = reader_.sliceBuilder.finish();
+ char[] name;
+ scanDirectiveNameToSlice(name, startMark);
- reader_.sliceBuilder.begin();
+ char[] value;
// Index where tag handle ends and suffix starts in a tag directive value.
uint tagHandleEnd = uint.max;
- if(name == "YAML") { scanYAMLDirectiveValueToSlice(startMark); }
- else if(name == "TAG") { tagHandleEnd = scanTagDirectiveValueToSlice(startMark); }
- char[] value = reader_.sliceBuilder.finish();
+ if(name == "YAML") { scanYAMLDirectiveValueToSlice(value, startMark); }
+ else if(name == "TAG") { tagHandleEnd = scanTagDirectiveValueToSlice(value, startMark); }
Mark endMark = reader_.mark;
@@ -891,55 +881,55 @@ struct Scanner
///
/// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice.
- void scanDirectiveNameToSlice(const Mark startMark) @safe
+ void scanDirectiveNameToSlice(ref char[] slice, const Mark startMark) @safe
{
// Scan directive name.
- scanAlphaNumericToSlice!"a directive"(startMark);
+ scanAlphaNumericToSlice!"directive"(slice, startMark);
enforce(reader_.peek().among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029'),
- new ScannerException("While scanning a directive", startMark,
- expected("alphanumeric, '-' or '_'", reader_.peek()), reader_.mark));
+ new ScannerException(expected("While scanning a directive, expected alphanumeric, '-' or '_'", reader_.peek()),
+ reader_.mark, "directive started here", startMark));
}
/// Scan value of a YAML directive token. Returns major, minor version separated by '.'.
///
/// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice.
- void scanYAMLDirectiveValueToSlice(const Mark startMark) @safe
+ void scanYAMLDirectiveValueToSlice(ref char[] slice, const Mark startMark) @safe
{
findNextNonSpace();
- scanYAMLDirectiveNumberToSlice(startMark);
+ scanYAMLDirectiveNumberToSlice(slice, startMark);
enforce(reader_.peekByte() == '.',
- new ScannerException("While scanning a directive", startMark,
- expected("digit or '.'", reader_.peek()), reader_.mark));
+ new ScannerException(expected("While scanning a directive, expected digit or '.'", reader_.peek()),
+ reader_.mark, "directive started here", startMark));
// Skip the '.'.
reader_.forward();
- reader_.sliceBuilder.write('.');
- scanYAMLDirectiveNumberToSlice(startMark);
+ slice ~= '.';
+ scanYAMLDirectiveNumberToSlice(slice, startMark);
enforce(reader_.peek().among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029'),
- new ScannerException("While scanning a directive", startMark,
- expected("digit or '.'", reader_.peek()), reader_.mark));
+ new ScannerException(expected("While scanning a directive, expected digit or '.'", reader_.peek()),
+ reader_.mark, "directive started here", startMark));
}
/// Scan a number from a YAML directive.
///
/// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice.
- void scanYAMLDirectiveNumberToSlice(const Mark startMark) @safe
+ void scanYAMLDirectiveNumberToSlice(ref char[] slice, const Mark startMark) @safe
{
enforce(isDigit(reader_.peek()),
- new ScannerException("While scanning a directive", startMark,
- expected("digit", reader_.peek()), reader_.mark));
+ new ScannerException(expected("While scanning a directive, expected a digit", reader_.peek()),
+ reader_.mark, "directive started here", startMark));
// Already found the first digit in the enforce(), so set length to 1.
uint length = 1;
while(reader_.peek(length).isDigit) { ++length; }
- reader_.sliceBuilder.write(reader_.get(length));
+ slice ~= reader_.get(length);
}
/// Scan value of a tag directive.
@@ -948,14 +938,14 @@ struct Scanner
/// characters into that slice.
///
/// Returns: Length of tag handle (which is before tag prefix) in scanned data
- uint scanTagDirectiveValueToSlice(const Mark startMark) @safe
+ uint scanTagDirectiveValueToSlice(ref char[] slice, const Mark startMark) @safe
{
findNextNonSpace();
- const startLength = reader_.sliceBuilder.length;
- scanTagDirectiveHandleToSlice(startMark);
- const handleLength = cast(uint)(reader_.sliceBuilder.length - startLength);
+ const startLength = slice.length;
+ scanTagDirectiveHandleToSlice(slice, startMark);
+ const handleLength = cast(uint)(slice.length - startLength);
findNextNonSpace();
- scanTagDirectivePrefixToSlice(startMark);
+ scanTagDirectivePrefixToSlice(slice, startMark);
return handleLength;
}
@@ -964,24 +954,24 @@ struct Scanner
///
/// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice.
- void scanTagDirectiveHandleToSlice(const Mark startMark) @safe
+ void scanTagDirectiveHandleToSlice(ref char[] slice, const Mark startMark) @safe
{
- scanTagHandleToSlice!"directive"(startMark);
+ scanTagHandleToSlice!"directive"(slice, startMark);
enforce(reader_.peekByte() == ' ',
- new ScannerException("While scanning a directive handle", startMark,
- expected("' '", reader_.peek()), reader_.mark));
+ new ScannerException(expected("While scanning a directive handle, expected ' '", reader_.peek()),
+ reader_.mark, "directive started here", startMark));
}
/// Scan prefix of a tag directive.
///
/// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice.
- void scanTagDirectivePrefixToSlice(const Mark startMark) @safe
+ void scanTagDirectivePrefixToSlice(ref char[] slice, const Mark startMark) @safe
{
- scanTagURIToSlice!"directive"(startMark);
+ scanTagURIToSlice!"directive"(slice, startMark);
enforce(reader_.peek().among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029'),
- new ScannerException("While scanning a directive prefix", startMark,
- expected("' '", reader_.peek()), reader_.mark));
+ new ScannerException(expected("While scanning a directive prefix, expected ' '", reader_.peek()),
+ reader_.mark, "directive started here", startMark));
}
/// Scan (and ignore) ignored line after a directive.
@@ -990,8 +980,8 @@ struct Scanner
findNextNonSpace();
if(reader_.peekByte() == '#') { scanToNextBreak(); }
enforce(reader_.peek().isBreak,
- new ScannerException("While scanning a directive", startMark,
- expected("comment or a line break", reader_.peek()), reader_.mark));
+ new ScannerException(expected("While scanning a directive, expected a comment or a line break", reader_.peek()),
+ reader_.mark, "directive started here", startMark));
scanLineBreak();
}
@@ -1011,10 +1001,7 @@ struct Scanner
const startMark = reader_.mark;
reader_.forward(); // The */& character was only peeked, so we drop it now
- reader_.sliceBuilder.begin();
- scanAnchorAliasToSlice(startMark);
- // On error, value is discarded as we return immediately
- char[] value = reader_.sliceBuilder.finish();
+ char[] value = readAnchorAlias(startMark);
assert(!reader_.peek().isNSAnchorName, "Anchor/alias name not fully scanned");
@@ -1035,8 +1022,7 @@ struct Scanner
const startMark = reader_.mark;
dchar c = reader_.peek(1);
- reader_.sliceBuilder.begin();
- scope(failure) { reader_.sliceBuilder.finish(); }
+ char[] slice;
// Index where tag handle ends and tag suffix starts in the tag value
// (slice) we will produce.
uint handleEnd;
@@ -1046,17 +1032,17 @@ struct Scanner
reader_.forward(2);
handleEnd = 0;
- scanTagURIToSlice!"tag"(startMark);
+ scanTagURIToSlice!"tag"(slice, startMark);
enforce(reader_.peekByte() == '>',
- new ScannerException("While scanning a tag", startMark,
- expected("'>'", reader_.peek()), reader_.mark));
+ new ScannerException(expected("While scanning a tag, expected a '>'", reader_.peek()),
+ reader_.mark, "tag started here", startMark));
reader_.forward();
}
else if(c.isWhiteSpace)
{
reader_.forward();
handleEnd = 0;
- reader_.sliceBuilder.write('!');
+ slice ~= '!';
}
else
{
@@ -1076,24 +1062,23 @@ struct Scanner
if(useHandle)
{
- scanTagHandleToSlice!"tag"(startMark);
- handleEnd = cast(uint)reader_.sliceBuilder.length;
+ scanTagHandleToSlice!"tag"(slice, startMark);
+ handleEnd = cast(uint)slice.length;
}
else
{
reader_.forward();
- reader_.sliceBuilder.write('!');
- handleEnd = cast(uint)reader_.sliceBuilder.length;
+ slice ~= '!';
+ handleEnd = cast(uint)slice.length;
}
- scanTagURIToSlice!"tag"(startMark);
+ scanTagURIToSlice!"tag"(slice, startMark);
}
enforce(reader_.peek().isBreakOrSpace,
- new ScannerException("While scanning a tag", startMark, expected("' '", reader_.peek()),
- reader_.mark));
+ new ScannerException(expected("While scanning a tag, expected a ' '", reader_.peek()),
+ reader_.mark, "tag started here", startMark));
- char[] slice = reader_.sliceBuilder.finish();
return tagToken(startMark, reader_.mark, slice, handleEnd);
}
@@ -1115,23 +1100,22 @@ struct Scanner
Mark endMark;
uint indent = max(1, indent_ + 1);
- reader_.sliceBuilder.begin();
- alias Transaction = SliceBuilder.Transaction;
+ char[] slice;
// Used to strip the last line breaks written to the slice at the end of the
// scalar, which may be needed based on chomping.
- Transaction breaksTransaction = Transaction(&reader_.sliceBuilder);
+ char[] newBreakSlice;
// Read the first indentation/line breaks before the scalar.
- size_t startLen = reader_.sliceBuilder.length;
+ size_t startLen = newBreakSlice.length;
if(increment == int.min)
{
- auto indentation = scanBlockScalarIndentationToSlice();
+ auto indentation = scanBlockScalarIndentationToSlice(newBreakSlice);
endMark = indentation[1];
indent = max(indent, indentation[0]);
}
else
{
indent += increment - 1;
- endMark = scanBlockScalarBreaksToSlice(indent);
+ endMark = scanBlockScalarBreaksToSlice(newBreakSlice, indent);
}
// int.max means there's no line break (int.max is outside UTF-32).
@@ -1140,24 +1124,22 @@ struct Scanner
// Scan the inner part of the block scalar.
while(reader_.column == indent && reader_.peekByte() != '\0')
{
- breaksTransaction.commit();
+ slice ~= newBreakSlice;
const bool leadingNonSpace = !reader_.peekByte().among!(' ', '\t');
// This is where the 'interesting' non-whitespace data gets read.
- scanToNextBreakToSlice();
+ scanToNextBreakToSlice(slice);
lineBreak = scanLineBreak();
// This transaction serves to rollback data read in the
// scanBlockScalarBreaksToSlice() call.
- breaksTransaction = Transaction(&reader_.sliceBuilder);
- startLen = reader_.sliceBuilder.length;
+ newBreakSlice = [];
+ startLen = slice.length;
// The line breaks should actually be written _after_ the if() block
// below. We work around that by inserting
- endMark = scanBlockScalarBreaksToSlice(indent);
+ endMark = scanBlockScalarBreaksToSlice(newBreakSlice, indent);
- // This will not run during the last iteration (see the if() vs the
- // while()), hence breaksTransaction rollback (which happens after this
- // loop) will never roll back data written in this if() block.
+ // This will not run during the last iteration
if(reader_.column == indent && reader_.peekByte() != '\0')
{
// Unfortunately, folding rules are ambiguous.
@@ -1168,16 +1150,16 @@ struct Scanner
{
// No breaks were scanned; no need to insert the space in the
// middle of slice.
- if(startLen == reader_.sliceBuilder.length)
+ if(startLen == slice.length + newBreakSlice.length)
{
- reader_.sliceBuilder.write(' ');
+ newBreakSlice ~= ' ';
}
}
else
{
// We need to insert in the middle of the slice in case any line
// breaks were scanned.
- reader_.sliceBuilder.insert(lineBreak, startLen);
+ newBreakSlice.insert(lineBreak, 0);
}
////this is Clark Evans's interpretation (also in the spec
@@ -1208,11 +1190,10 @@ struct Scanner
}
}
- // If chompint is Keep, we keep (commit) the last scanned line breaks
+ // If chomping is Keep, we keep (commit) the last scanned line breaks
// (which are at the end of the scalar). Otherwise re remove them (end the
// transaction).
- if(chomping == Chomping.keep) { breaksTransaction.commit(); }
- else { breaksTransaction.end(); }
+ if(chomping == Chomping.keep) { slice ~= newBreakSlice; }
if(chomping != Chomping.strip && lineBreak != int.max)
{
// If chomping is Keep, we keep the line break but the first line break
@@ -1220,21 +1201,18 @@ struct Scanner
// be inserted _before_ the other line breaks.
if(chomping == Chomping.keep)
{
- reader_.sliceBuilder.insert(lineBreak, startLen);
+ slice.insert(lineBreak, startLen);
}
- // If chomping is not Keep, breaksTransaction was cancelled so we can
- // directly write the first line break (as it isn't stripped - chomping
- // is not Strip)
+ // If chomping is not Keep, discard the line break
else
{
if (lineBreak != '\0')
{
- reader_.sliceBuilder.write(lineBreak);
+ slice ~= lineBreak;
}
}
}
- char[] slice = reader_.sliceBuilder.finish();
return scalarToken(startMark, endMark, slice, style);
}
@@ -1257,8 +1235,8 @@ struct Scanner
}
enforce(c.among!(' ', '\0', '\n', '\r', '\u0085', '\u2028', '\u2029'),
- new ScannerException("While scanning a block scalar", startMark,
- expected("chomping or indentation indicator", c), reader_.mark));
+ new ScannerException(expected("While scanning a block scalar, expected a chomping or indentation indicator", c),
+ reader_.mark, "scalar started here", startMark));
return tuple(chomping, increment);
}
@@ -1299,8 +1277,8 @@ struct Scanner
assert(increment < 10 && increment >= 0, "Digit has invalid value");
enforce(increment > 0,
- new ScannerException("While scanning a block scalar", startMark,
- expected("indentation indicator in range 1-9", "0"), reader_.mark));
+ new ScannerException(expected("While scanning a block scalar, expected an indentation indicator in range 1-9", "0"),
+ reader_.mark, "scalar started here", startMark));
reader_.forward();
c = reader_.peek();
@@ -1314,8 +1292,8 @@ struct Scanner
if(reader_.peekByte()== '#') { scanToNextBreak(); }
enforce(reader_.peek().isBreak,
- new ScannerException("While scanning a block scalar", startMark,
- expected("comment or line break", reader_.peek()), reader_.mark));
+ new ScannerException(expected("While scanning a block scalar, expected a comment or line break", reader_.peek()),
+ reader_.mark, "scalar started here", startMark));
scanLineBreak();
}
@@ -1324,7 +1302,7 @@ struct Scanner
///
/// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice.
- Tuple!(uint, Mark) scanBlockScalarIndentationToSlice() @safe
+ Tuple!(uint, Mark) scanBlockScalarIndentationToSlice(ref char[] slice) @safe
{
uint maxIndent;
Mark endMark = reader_.mark;
@@ -1333,7 +1311,7 @@ struct Scanner
{
if(reader_.peekByte() != ' ')
{
- reader_.sliceBuilder.write(scanLineBreak());
+ slice ~= scanLineBreak();
endMark = reader_.mark;
continue;
}
@@ -1348,7 +1326,7 @@ struct Scanner
///
/// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice.
- Mark scanBlockScalarBreaksToSlice(const uint indent) @safe
+ Mark scanBlockScalarBreaksToSlice(ref char[] slice, const uint indent) @safe
{
Mark endMark = reader_.mark;
@@ -1356,7 +1334,7 @@ struct Scanner
{
while(reader_.column < indent && reader_.peekByte() == ' ') { reader_.forward(); }
if(!reader_.peek().among!('\n', '\r', '\u0085', '\u2028', '\u2029')) { break; }
- reader_.sliceBuilder.write(scanLineBreak());
+ slice ~= scanLineBreak();
endMark = reader_.mark;
}
@@ -1369,18 +1347,17 @@ struct Scanner
const startMark = reader_.mark;
const quote = reader_.get();
- reader_.sliceBuilder.begin();
+ char[] slice;
- scanFlowScalarNonSpacesToSlice(quotes, startMark);
+ scanFlowScalarNonSpacesToSlice(slice, quotes, startMark);
while(reader_.peek() != quote)
{
- scanFlowScalarSpacesToSlice(startMark);
- scanFlowScalarNonSpacesToSlice(quotes, startMark);
+ scanFlowScalarSpacesToSlice(slice, startMark);
+ scanFlowScalarNonSpacesToSlice(slice, quotes, startMark);
}
reader_.forward();
- auto slice = reader_.sliceBuilder.finish();
return scalarToken(startMark, reader_.mark, slice, quotes);
}
@@ -1388,7 +1365,7 @@ struct Scanner
///
/// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice.
- void scanFlowScalarNonSpacesToSlice(const ScalarStyle quotes, const Mark startMark)
+ void scanFlowScalarNonSpacesToSlice(ref char[] slice, const ScalarStyle quotes, const Mark startMark)
@safe
{
for(;;)
@@ -1398,19 +1375,19 @@ struct Scanner
size_t numCodePoints;
while(!reader_.peek(numCodePoints).isFlowScalarBreakSpace) { ++numCodePoints; }
- if (numCodePoints > 0) { reader_.sliceBuilder.write(reader_.get(numCodePoints)); }
+ if (numCodePoints > 0) { slice ~= reader_.get(numCodePoints); }
c = reader_.peek();
if(quotes == ScalarStyle.singleQuoted && c == '\'' && reader_.peek(1) == '\'')
{
reader_.forward(2);
- reader_.sliceBuilder.write('\'');
+ slice ~= '\'';
}
else if((quotes == ScalarStyle.doubleQuoted && c == '\'') ||
(quotes == ScalarStyle.singleQuoted && c.among!('"', '\\')))
{
reader_.forward();
- reader_.sliceBuilder.write(c);
+ slice ~= c;
}
else if(quotes == ScalarStyle.doubleQuoted && c == '\\')
{
@@ -1423,7 +1400,7 @@ struct Scanner
// place (in a slice) in case of '\P' and '\L' (very uncommon,
// but we don't want to break the spec)
char[2] escapeSequence = ['\\', cast(char)c];
- reader_.sliceBuilder.write(escapeSequence);
+ slice ~= escapeSequence;
}
else if(c.among!(escapeHexCodeList))
{
@@ -1432,32 +1409,27 @@ struct Scanner
foreach(i; 0 .. hexLength) {
enforce(reader_.peek(i).isHexDigit,
- new ScannerException("While scanning a double quoted scalar", startMark,
- expected("escape sequence of hexadecimal numbers",
- reader_.peek(i)), reader_.mark));
+ new ScannerException(expected("While scanning a double quoted scalar, expected an escape sequence of hexadecimal numbers", reader_.peek(i)),
+ reader_.mark, "scalar started here", startMark));
}
char[] hex = reader_.get(hexLength);
- enforce((hex.length > 0) && (hex.length <= 8),
- new ScannerException("While scanning a double quoted scalar", startMark,
- "overflow when parsing an escape sequence of " ~
- "hexadecimal numbers.", reader_.mark));
+ assert((hex.length > 0) && (hex.length <= 8), "Hex escape overflow");
char[2] escapeStart = ['\\', cast(char) c];
- reader_.sliceBuilder.write(escapeStart);
- reader_.sliceBuilder.write(hex);
+ slice ~= escapeStart;
+ slice ~= hex;
}
else if(c.among!('\n', '\r', '\u0085', '\u2028', '\u2029'))
{
scanLineBreak();
- scanFlowScalarBreaksToSlice(startMark);
+ scanFlowScalarBreaksToSlice(slice, startMark);
}
else
{
- throw new ScannerException("While scanning a double quoted scalar", startMark,
- text("found unsupported escape character ", c),
- reader_.mark);
+ throw new ScannerException(text("While scanning a double quoted scalar, found unsupported escape character ", c),
+ reader_.mark, "scalar started here", startMark);
}
}
else { return; }
@@ -1468,7 +1440,7 @@ struct Scanner
///
/// Assumes that the caller is building a slice in Reader, and puts the scanned
/// spaces into that slice.
- void scanFlowScalarSpacesToSlice(const Mark startMark) @safe
+ void scanFlowScalarSpacesToSlice(ref char[] slice, const Mark startMark) @safe
{
// Increase length as long as we see whitespace.
size_t length;
@@ -1478,14 +1450,14 @@ struct Scanner
// Can check the last byte without striding because '\0' is ASCII
const c = reader_.peek(length);
enforce(c != '\0',
- new ScannerException("While scanning a quoted scalar", startMark,
- "found unexpected end of buffer", reader_.mark));
+ new ScannerException("While scanning a quoted scalar, found unexpected end of buffer",
+ reader_.mark, "scalar started here", startMark));
// Spaces not followed by a line break.
if(!c.among!('\n', '\r', '\u0085', '\u2028', '\u2029'))
{
reader_.forward(length);
- reader_.sliceBuilder.write(whitespaces);
+ slice ~= whitespaces;
return;
}
@@ -1493,21 +1465,21 @@ struct Scanner
reader_.forward(length);
const lineBreak = scanLineBreak();
- if(lineBreak != '\n') { reader_.sliceBuilder.write(lineBreak); }
+ if(lineBreak != '\n') { slice ~= lineBreak; }
// If we have extra line breaks after the first, scan them into the
// slice.
- const bool extraBreaks = scanFlowScalarBreaksToSlice(startMark);
+ const bool extraBreaks = scanFlowScalarBreaksToSlice(slice, startMark);
// No extra breaks, one normal line break. Replace it with a space.
- if(lineBreak == '\n' && !extraBreaks) { reader_.sliceBuilder.write(' '); }
+ if(lineBreak == '\n' && !extraBreaks) { slice ~= ' '; }
}
/// Scan line breaks in a flow scalar.
///
/// Assumes that the caller is building a slice in Reader, and puts the scanned
/// line breaks into that slice.
- bool scanFlowScalarBreaksToSlice(const Mark startMark) @safe
+ bool scanFlowScalarBreaksToSlice(ref char[] slice, const Mark startMark) @safe
{
// True if at least one line break was found.
bool anyBreaks;
@@ -1517,8 +1489,8 @@ struct Scanner
const prefix = reader_.prefix(3);
enforce(!(prefix == "---" || prefix == "...") ||
!reader_.peek(3).isWhiteSpace,
- new ScannerException("While scanning a quoted scalar", startMark,
- "found unexpected document separator", reader_.mark));
+ new ScannerException("While scanning a quoted scalar, found unexpected document separator",
+ reader_.mark, "scalar started here", startMark));
// Skip any whitespaces.
while(reader_.peekByte().among!(' ', '\t')) { reader_.forward(); }
@@ -1528,7 +1500,7 @@ struct Scanner
const lineBreak = scanLineBreak();
anyBreaks = true;
- reader_.sliceBuilder.write(lineBreak);
+ slice ~= lineBreak;
}
return anyBreaks;
}
@@ -1546,10 +1518,9 @@ struct Scanner
// document separators at the beginning of the line.
// if(indent == 0) { indent = 1; }
- reader_.sliceBuilder.begin();
+ char[] slice;
- alias Transaction = SliceBuilder.Transaction;
- Transaction spacesTransaction;
+ char[] newSpacesSlice;
// Stop at a comment.
while(reader_.peekByte() != '#')
{
@@ -1561,7 +1532,8 @@ struct Scanner
const cNext = reader_.peek(length + 1);
if(c.isWhiteSpace ||
(flowLevel_ == 0 && c == ':' && cNext.isWhiteSpace) ||
- (flowLevel_ > 0 && c.among!(',', ':', '?', '[', ']', '{', '}')))
+ (flowLevel_ > 0 && c == ':' && (cNext.isWhiteSpace || cNext.among!(',', '[', ']', '{', '}'))) ||
+ (flowLevel_ > 0 && c.among!(',', '[', ']', '{', '}')))
{
break;
}
@@ -1569,38 +1541,26 @@ struct Scanner
c = cNext;
}
- // It's not clear what we should do with ':' in the flow context.
- enforce(flowLevel_ == 0 || c != ':' ||
- reader_.peek(length + 1).isWhiteSpace ||
- reader_.peek(length + 1).among!(',', '[', ']', '{', '}'),
- new ScannerException("While scanning a plain scalar", startMark,
- "found unexpected ':' . Please check " ~
- "http://pyyaml.org/wiki/YAMLColonInFlowContext for details.",
- reader_.mark));
-
if(length == 0) { break; }
allowSimpleKey_ = false;
- reader_.sliceBuilder.write(reader_.get(length));
+ newSpacesSlice ~= reader_.get(length);
endMark = reader_.mark;
- spacesTransaction.commit();
- spacesTransaction = Transaction(&reader_.sliceBuilder);
+ slice ~= newSpacesSlice;
+ newSpacesSlice = [];
- const startLength = reader_.sliceBuilder.length;
- scanPlainSpacesToSlice();
- if(startLength == reader_.sliceBuilder.length ||
+ const startLength = slice.length;
+ scanPlainSpacesToSlice(newSpacesSlice);
+ if(startLength == slice.length + newSpacesSlice.length ||
(flowLevel_ == 0 && reader_.column < indent))
{
break;
}
}
- spacesTransaction.end();
- char[] slice = reader_.sliceBuilder.finish();
-
return scalarToken(startMark, endMark, slice, ScalarStyle.plain);
}
@@ -1608,7 +1568,7 @@ struct Scanner
///
/// Assumes that the caller is building a slice in Reader, and puts the spaces
/// into that slice.
- void scanPlainSpacesToSlice() @safe
+ void scanPlainSpacesToSlice(ref char[] slice) @trusted
{
// The specification is really confusing about tabs in plain scalars.
// We just forbid them completely. Do not use tabs in YAML!
@@ -1623,7 +1583,7 @@ struct Scanner
if(!c.isNSChar)
{
// We have spaces, but no newline.
- if(whitespaces.length > 0) { reader_.sliceBuilder.write(whitespaces); }
+ if(whitespaces.length > 0) { slice ~= whitespaces; }
return;
}
@@ -1642,9 +1602,8 @@ struct Scanner
bool extraBreaks;
- alias Transaction = SliceBuilder.Transaction;
- auto transaction = Transaction(&reader_.sliceBuilder);
- if(lineBreak != '\n') { reader_.sliceBuilder.write(lineBreak); }
+ char[] newSlice;
+ if(lineBreak != '\n') { newSlice ~= lineBreak; }
while(reader_.peek().isNSChar)
{
if(reader_.peekByte() == ' ') { reader_.forward(); }
@@ -1652,27 +1611,28 @@ struct Scanner
{
const lBreak = scanLineBreak();
extraBreaks = true;
- reader_.sliceBuilder.write(lBreak);
+ newSlice ~= lBreak;
if(end(reader_)) { return; }
}
}
- transaction.commit();
+ slice ~= newSlice;
// No line breaks, only a space.
- if(lineBreak == '\n' && !extraBreaks) { reader_.sliceBuilder.write(' '); }
+ if(lineBreak == '\n' && !extraBreaks) { slice ~= ' '; }
}
/// Scan handle of a tag token.
///
/// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice.
- void scanTagHandleToSlice(string name)(const Mark startMark)
+ void scanTagHandleToSlice(string name)(ref char[] slice, const Mark startMark)
{
dchar c = reader_.peek();
- enum contextMsg = "While scanning a " ~ name;
+ enum contextMsg = "While scanning a " ~ name ~ ", expected a !";
+ // should this be an assert?
enforce(c == '!',
- new ScannerException(contextMsg, startMark, expected("'!'", c), reader_.mark));
+ new ScannerException(expected(contextMsg, c), reader_.mark, "tag started here", startMark));
uint length = 1;
c = reader_.peek(length);
@@ -1684,22 +1644,22 @@ struct Scanner
c = reader_.peek(length);
}
enforce(c == '!',
- new ScannerException(contextMsg, startMark, expected("'!'", c), reader_.mark));
+ new ScannerException(expected(contextMsg, c), reader_.mark(length), "tag started here", startMark));
++length;
}
- reader_.sliceBuilder.write(reader_.get(length));
+ slice ~= reader_.get(length);
}
/// Scan URI in a tag token.
///
/// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice.
- void scanTagURIToSlice(string name)(const Mark startMark)
+ void scanTagURIToSlice(string name)(ref char[] slice, const Mark startMark)
{
// Note: we do not check if URI is well-formed.
dchar c = reader_.peek();
- const startLen = reader_.sliceBuilder.length;
+ const startLen = slice.length;
{
uint length;
while(c.isAlphaNum || c.isURIChar)
@@ -1707,9 +1667,9 @@ struct Scanner
if(c == '%')
{
auto chars = reader_.get(length);
- reader_.sliceBuilder.write(chars);
+ slice ~= chars;
length = 0;
- scanURIEscapesToSlice!name(startMark);
+ scanURIEscapesToSlice!name(slice, startMark);
}
else { ++length; }
c = reader_.peek(length);
@@ -1717,14 +1677,14 @@ struct Scanner
if(length > 0)
{
auto chars = reader_.get(length);
- reader_.sliceBuilder.write(chars);
+ slice ~= chars;
length = 0;
}
}
// OK if we scanned something, error otherwise.
- enum contextMsg = "While parsing a " ~ name;
- enforce(reader_.sliceBuilder.length > startLen,
- new ScannerException(contextMsg, startMark, expected("URI", c), reader_.mark));
+ enum contextMsg = "While parsing a " ~ name ~ ", expected a URI";
+ enforce(slice.length > startLen,
+ new ScannerException(expected(contextMsg, c), reader_.mark, "tag started here", startMark));
}
// Not @nogc yet because std.utf.decode is not @nogc
@@ -1732,7 +1692,7 @@ struct Scanner
///
/// Assumes that the caller is building a slice in Reader, and puts the scanned
/// characters into that slice.
- void scanURIEscapesToSlice(string name)(const Mark startMark)
+ void scanURIEscapesToSlice(string name)(ref char[] slice, const Mark startMark)
{
import core.exception : UnicodeException;
// URI escapes encode a UTF-8 string. We store UTF-8 code units here for
@@ -1747,9 +1707,8 @@ struct Scanner
char[2] nextByte = [reader_.peekByte(), reader_.peekByte(1)];
enforce(nextByte[0].isHexDigit && nextByte[1].isHexDigit,
- new ScannerException(contextMsg, startMark,
- expected("URI escape sequence of 2 hexadecimal " ~
- "numbers", nextByte), reader_.mark));
+ new ScannerException(expected(contextMsg ~ ", expected a URI escape sequence of 2 hexadecimal numbers", nextByte),
+ reader_.mark, "tag started here", startMark));
buffer ~= nextByte[].to!ubyte(16);
@@ -1759,14 +1718,13 @@ struct Scanner
{
foreach (dchar chr; buffer.data)
{
- reader_.sliceBuilder.write(chr);
+ slice ~= chr;
}
}
catch (UnicodeException)
{
- throw new ScannerException(contextMsg, startMark,
- "Invalid UTF-8 data encoded in URI escape sequence",
- reader_.mark);
+ throw new ScannerException(contextMsg ~ ", found invalid UTF-8 data encoded in URI escape sequence",
+ reader_.mark, "tag started here", startMark);
}
}
@@ -1811,16 +1769,22 @@ struct Scanner
}
}
-// Issue 309 - https://github.com/dlang-community/D-YAML/issues/309
-@safe unittest
+private void insert(ref char[] slice, const dchar c, const size_t position) @safe pure
+in(position <= slice.length, text("Trying to insert after the end of the slice (", position, " > ", slice.length, ")"))
{
- enum str = q"EOS
-exp: |
- foobar
-EOS".chomp;
-
- auto r = new Reader(cast(ubyte[])str.dup);
- auto s = Scanner(r);
- auto elems = s.map!"a.value".filter!"a.length > 0".array;
- assert(elems[1] == "foobar");
+ const point = position;
+ const movedLength = slice.length - point;
+
+ // Encode c into UTF-8
+ char[4] encodeBuf;
+ if(c < 0x80) { encodeBuf[0] = cast(char)c; }
+ const size_t bytes = c < 0x80 ? 1 : encode(encodeBuf, c);
+
+ slice.length += bytes;
+ if(movedLength > 0)
+ {
+ copy(slice[point..point + movedLength * char.sizeof],
+ slice[point + bytes .. point + bytes + movedLength * char.sizeof]);
+ }
+ slice[point .. point + bytes] = encodeBuf[0 .. bytes];
}
diff --git a/src/ext_depends/D-YAML/source/dyaml/test/common.d b/src/ext_depends/D-YAML/source/dyaml/test/common.d
deleted file mode 100644
index a6bafa9..0000000
--- a/src/ext_depends/D-YAML/source/dyaml/test/common.d
+++ /dev/null
@@ -1,223 +0,0 @@
-
-// 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)
-
-module dyaml.test.common;
-
-version(unittest)
-{
-
-import dyaml.node;
-import dyaml.event;
-
-import core.exception;
-import std.algorithm;
-import std.array;
-import std.conv;
-import std.file;
-import std.range;
-import std.path;
-import std.traits;
-import std.typecons;
-
-package:
-
-/**
-Run a test.
-
-Params:
- testFunction = Unittest function.
- unittestExt = Extensions of data files needed for the unittest.
- skipExt = Extensions that must not be used for the unittest.
- */
-void run(D)(D testFunction, string[] unittestExt, string[] skipExt = [])
-{
- immutable string dataDir = __FILE_FULL_PATH__.dirName ~ "/../../../test/data";
- auto testFilenames = findTestFilenames(dataDir);
-
- if (unittestExt.length > 0)
- {
- outer: foreach (base, extensions; testFilenames)
- {
- string[] filenames;
- foreach (ext; unittestExt)
- {
- if (!extensions.canFind(ext))
- {
- continue outer;
- }
- filenames ~= base ~ '.' ~ ext;
- }
- foreach (ext; skipExt)
- {
- if (extensions.canFind(ext))
- {
- continue outer;
- }
- }
-
- execute(testFunction, filenames);
- }
- }
- else
- {
- execute(testFunction, string[].init);
- }
-}
-
-// TODO: remove when a @safe ubyte[] file read can be done.
-/**
-Reads a file as an array of bytes.
-
-Params:
- filename = Full path to file to read.
-
-Returns: The file's data.
-*/
-ubyte[] readData(string filename) @trusted
-{
- import std.file : read;
- return cast(ubyte[])read(filename);
-}
-void assertNodesEqual(const scope Node gotNode, const scope Node expectedNode) @safe
-{
- import std.format : format;
- assert(gotNode == expectedNode, format!"got %s, expected %s"(gotNode.debugString, expectedNode.debugString));
-}
-
-/**
-Determine if events in events1 are equivalent to events in events2.
-
-Params:
- events1 = A range of events to compare with.
- events2 = A second range of events to compare.
-
-Returns: true if the events are equivalent, false otherwise.
-*/
-bool compareEvents(T, U)(T events1, U events2)
-if (isInputRange!T && isInputRange!U && is(ElementType!T == Event) && is(ElementType!U == Event))
-{
- foreach (e1, e2; zip(events1, events2))
- {
- //Different event types.
- if (e1.id != e2.id)
- {
- return false;
- }
- //Different anchor (if applicable).
- if (e1.id.among!(EventID.sequenceStart, EventID.mappingStart, EventID.alias_, EventID.scalar)
- && e1.anchor != e2.anchor)
- {
- return false;
- }
- //Different collection tag (if applicable).
- if (e1.id.among!(EventID.sequenceStart, EventID.mappingStart) && e1.tag != e2.tag)
- {
- return false;
- }
- if (e1.id == EventID.scalar)
- {
- //Different scalar tag (if applicable).
- if (!(e1.implicit || e2.implicit) && e1.tag != e2.tag)
- {
- return false;
- }
- //Different scalar value.
- if (e1.value != e2.value)
- {
- return false;
- }
- }
- }
- return true;
-}
-/**
-Throw an Error if events in events1 aren't equivalent to events in events2.
-
-Params:
- events1 = First event array to compare.
- events2 = Second event array to compare.
-*/
-void assertEventsEqual(T, U)(T events1, U events2)
-if (isInputRange!T && isInputRange!U && is(ElementType!T == Event) && is(ElementType!U == Event))
-{
- auto events1Copy = events1.array;
- auto events2Copy = events2.array;
- assert(compareEvents(events1Copy, events2Copy), text("Got '", events1Copy, "', expected '", events2Copy, "'"));
-}
-
-private:
-
-/**
-Find unittest input filenames.
-
-Params: dir = Directory to look in.
-
-Returns: Test input base filenames and their extensions.
-*/
- //@trusted due to dirEntries
-string[][string] findTestFilenames(const string dir) @trusted
-{
- //Groups of extensions indexed by base names.
- string[][string] names;
- foreach (string name; dirEntries(dir, SpanMode.shallow))
- {
- if (isFile(name))
- {
- string base = name.stripExtension();
- string ext = name.extension();
- if (ext is null)
- {
- ext = "";
- }
- if (ext[0] == '.')
- {
- ext = ext[1 .. $];
- }
-
- //If the base name doesn't exist yet, add it; otherwise add new extension.
- names[base] = ((base in names) is null) ? [ext] : names[base] ~ ext;
- }
- }
- return names;
-}
-
-/**
-Recursively copy an array of strings to a tuple to use for unittest function input.
-
-Params:
- index = Current index in the array/tuple.
- tuple = Tuple to copy to.
- strings = Strings to copy.
-*/
-void stringsToTuple(uint index, F ...)(ref F tuple, const string[] strings)
-in(F.length == strings.length)
-do
-{
- tuple[index] = strings[index];
- static if (index > 0)
- {
- stringsToTuple!(index - 1, F)(tuple, strings);
- }
-}
-
-/**
-Execute an unittest on specified files.
-
-Params:
- testName = Name of the unittest.
- testFunction = Unittest function.
- filenames = Names of input files to test with.
- */
-void execute(D)(D testFunction, string[] filenames)
-{
- //Convert filenames to parameters tuple and call the test function.
- alias F = Parameters!D[0..$];
- F parameters;
- stringsToTuple!(F.length - 1, F)(parameters, filenames);
- testFunction(parameters);
-}
-
-} // version(unittest)
diff --git a/src/ext_depends/D-YAML/source/dyaml/test/compare.d b/src/ext_depends/D-YAML/source/dyaml/test/compare.d
deleted file mode 100644
index 5a37fd0..0000000
--- a/src/ext_depends/D-YAML/source/dyaml/test/compare.d
+++ /dev/null
@@ -1,51 +0,0 @@
-
-// 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)
-
-module dyaml.test.compare;
-
-@safe unittest
-{
- import dyaml : Loader;
- import dyaml.test.common : assertNodesEqual, compareEvents, run;
-
- /**
- Test parser by comparing output from parsing two equivalent YAML files.
-
- Params:
- dataFilename = YAML file to parse.
- canonicalFilename = Another file to parse, in canonical YAML format.
- */
- static void testParser(string dataFilename, string canonicalFilename) @safe
- {
- auto dataEvents = Loader.fromFile(dataFilename).parse();
- auto canonicalEvents = Loader.fromFile(canonicalFilename).parse();
-
- //BUG: the return value isn't checked! This test currently fails...
- compareEvents(dataEvents, canonicalEvents);
- }
-
- /**
- Test loader by comparing output from loading two equivalent YAML files.
-
- Params:
- dataFilename = YAML file to load.
- canonicalFilename = Another file to load, in canonical YAML format.
- */
- static void testLoader(string dataFilename, string canonicalFilename) @safe
- {
- import std.array : array;
- auto data = Loader.fromFile(dataFilename).array;
- auto canonical = Loader.fromFile(canonicalFilename).array;
-
- assert(data.length == canonical.length, "Unequal node count");
- foreach (n; 0 .. data.length)
- {
- assertNodesEqual(data[n], canonical[n]);
- }
- }
- run(&testParser, ["data", "canonical"]);
- run(&testLoader, ["data", "canonical"], ["test_loader_skip"]);
-}
diff --git a/src/ext_depends/D-YAML/source/dyaml/test/constructor.d b/src/ext_depends/D-YAML/source/dyaml/test/constructor.d
index aeb8653..ad9fb62 100644
--- a/src/ext_depends/D-YAML/source/dyaml/test/constructor.d
+++ b/src/ext_depends/D-YAML/source/dyaml/test/constructor.d
@@ -6,10 +6,9 @@
module dyaml.test.constructor;
+package version(unittest):
-version(unittest)
-{
-
+import std.algorithm;
import std.conv;
import std.datetime;
import std.exception;
@@ -920,38 +919,3 @@ struct TestStruct
return Node(value.to!string, "!tag2");
}
}
-
-} // version(unittest)
-
-
-@safe unittest
-{
- import dyaml.test.common : assertNodesEqual, run;
- /**
- Constructor unittest.
-
- Params:
- dataFilename = File name to read from.
- codeDummy = Dummy .code filename, used to determine that
- .data file with the same name should be used in this test.
- */
- static void testConstructor(string dataFilename, string codeDummy) @safe
- {
- string base = dataFilename.baseName.stripExtension;
- assert((base in expected) !is null, "Unimplemented constructor test: " ~ base);
-
- auto loader = Loader.fromFile(dataFilename);
-
- Node[] exp = expected[base];
-
- //Compare with expected results document by document.
- size_t i;
- foreach (node; loader)
- {
- assertNodesEqual(node, exp[i]);
- ++i;
- }
- assert(i == exp.length);
- }
- run(&testConstructor, ["data", "code"]);
-}
diff --git a/src/ext_depends/D-YAML/source/dyaml/test/emitter.d b/src/ext_depends/D-YAML/source/dyaml/test/emitter.d
deleted file mode 100644
index 293f236..0000000
--- a/src/ext_depends/D-YAML/source/dyaml/test/emitter.d
+++ /dev/null
@@ -1,132 +0,0 @@
-
-// Copyright Ferdinand Majerech 2011-2014.
-// 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)
-
-module dyaml.test.emitter;
-
-@safe unittest
-{
- import std.array : Appender;
- import std.range : ElementType, isInputRange;
-
- import dyaml : CollectionStyle, LineBreak, Loader, Mark, ScalarStyle;
- import dyaml.emitter : Emitter;
- import dyaml.event : Event, EventID, mappingStartEvent, scalarEvent, sequenceStartEvent;
- import dyaml.test.common : assertEventsEqual, run;
-
- // Try to emit an event range.
- static void emitTestCommon(T)(ref Appender!string emitStream, T events, bool canonical = false) @safe
- if (isInputRange!T && is(ElementType!T == Event))
- {
- auto emitter = Emitter!(typeof(emitStream), char)(emitStream, canonical, 2, 80, LineBreak.unix);
- foreach (ref event; events)
- {
- emitter.emit(event);
- }
- }
- /**
- Test emitter by getting events from parsing a file, emitting them, parsing
- the emitted result and comparing events from parsing the emitted result with
- originally parsed events.
-
- Params:
- dataFilename = YAML file to parse.
- canonicalFilename = Canonical YAML file used as dummy to determine
- which data files to load.
- */
- static void testEmitterOnData(string dataFilename, string canonicalFilename) @safe
- {
- //Must exist due to Anchor, Tags reference counts.
- auto loader = Loader.fromFile(dataFilename);
- auto events = loader.parse();
- auto emitStream = Appender!string();
- emitTestCommon(emitStream, events);
-
- auto loader2 = Loader.fromString(emitStream.data);
- loader2.name = "TEST";
- auto newEvents = loader2.parse();
- assertEventsEqual(events, newEvents);
- }
- /**
- Test emitter by getting events from parsing a canonical YAML file, emitting
- them both in canonical and normal format, parsing the emitted results and
- comparing events from parsing the emitted result with originally parsed events.
-
- Params: canonicalFilename = Canonical YAML file to parse.
- */
- static void testEmitterOnCanonical(string canonicalFilename) @safe
- {
- //Must exist due to Anchor, Tags reference counts.
- auto loader = Loader.fromFile(canonicalFilename);
- auto events = loader.parse();
- foreach (canonical; [false, true])
- {
- auto emitStream = Appender!string();
- emitTestCommon(emitStream, events, canonical);
-
- auto loader2 = Loader.fromString(emitStream.data);
- loader2.name = "TEST";
- auto newEvents = loader2.parse();
- assertEventsEqual(events, newEvents);
- }
- }
- /**
- Test emitter by getting events from parsing a file, emitting them with all
- possible scalar and collection styles, parsing the emitted results and
- comparing events from parsing the emitted result with originally parsed events.
-
- Params:
- dataFilename = YAML file to parse.
- canonicalFilename = Canonical YAML file used as dummy to determine
- which data files to load.
- */
- static void testEmitterStyles(string dataFilename, string canonicalFilename) @safe
- {
- foreach (filename; [dataFilename, canonicalFilename])
- {
- //must exist due to Anchor, Tags reference counts
- auto loader = Loader.fromFile(canonicalFilename);
- auto events = loader.parse();
- foreach (flowStyle; [CollectionStyle.block, CollectionStyle.flow])
- {
- foreach (style; [ScalarStyle.literal, ScalarStyle.folded,
- ScalarStyle.doubleQuoted, ScalarStyle.singleQuoted,
- ScalarStyle.plain])
- {
- Event[] styledEvents;
- foreach (event; events)
- {
- if (event.id == EventID.scalar)
- {
- event = scalarEvent(Mark(), Mark(), event.anchor, event.tag,
- event.implicit,
- event.value, style);
- }
- else if (event.id == EventID.sequenceStart)
- {
- event = sequenceStartEvent(Mark(), Mark(), event.anchor,
- event.tag, event.implicit, flowStyle);
- }
- else if (event.id == EventID.mappingStart)
- {
- event = mappingStartEvent(Mark(), Mark(), event.anchor,
- event.tag, event.implicit, flowStyle);
- }
- styledEvents ~= event;
- }
- auto emitStream = Appender!string();
- emitTestCommon(emitStream, styledEvents);
- auto loader2 = Loader.fromString(emitStream.data);
- loader2.name = "TEST";
- auto newEvents = loader2.parse();
- assertEventsEqual(events, newEvents);
- }
- }
- }
- }
- run(&testEmitterOnData, ["data", "canonical"]);
- run(&testEmitterOnCanonical, ["canonical"]);
- run(&testEmitterStyles, ["data", "canonical"]);
-}
diff --git a/src/ext_depends/D-YAML/source/dyaml/test/errors.d b/src/ext_depends/D-YAML/source/dyaml/test/errors.d
deleted file mode 100644
index 43b019c..0000000
--- a/src/ext_depends/D-YAML/source/dyaml/test/errors.d
+++ /dev/null
@@ -1,64 +0,0 @@
-
-// Copyright Ferdinand Majerech 2011-2014
-// 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)
-
-module dyaml.test.errors;
-
-@safe unittest
-{
- import std.array : array;
- import std.exception : assertThrown;
-
- import dyaml : Loader;
- import dyaml.test.common : run;
-
- /**
- Loader error unittest from file stream.
-
- Params: errorFilename = File name to read from.
- */
- static void testLoaderError(string errorFilename) @safe
- {
- assertThrown(Loader.fromFile(errorFilename).array,
- __FUNCTION__ ~ "(" ~ errorFilename ~ ") Expected an exception");
- }
-
- /**
- Loader error unittest from string.
-
- Params: errorFilename = File name to read from.
- */
- static void testLoaderErrorString(string errorFilename) @safe
- {
- assertThrown(Loader.fromFile(errorFilename).array,
- __FUNCTION__ ~ "(" ~ errorFilename ~ ") Expected an exception");
- }
-
- /**
- Loader error unittest from filename.
-
- Params: errorFilename = File name to read from.
- */
- static void testLoaderErrorFilename(string errorFilename) @safe
- {
- assertThrown(Loader.fromFile(errorFilename).array,
- __FUNCTION__ ~ "(" ~ errorFilename ~ ") Expected an exception");
- }
-
- /**
- Loader error unittest loading a single document from a file.
-
- Params: errorFilename = File name to read from.
- */
- static void testLoaderErrorSingle(string errorFilename) @safe
- {
- assertThrown(Loader.fromFile(errorFilename).load(),
- __FUNCTION__ ~ "(" ~ errorFilename ~ ") Expected an exception");
- }
- run(&testLoaderError, ["loader-error"]);
- run(&testLoaderErrorString, ["loader-error"]);
- run(&testLoaderErrorFilename, ["loader-error"]);
- run(&testLoaderErrorSingle, ["single-loader-error"]);
-}
diff --git a/src/ext_depends/D-YAML/source/dyaml/test/inputoutput.d b/src/ext_depends/D-YAML/source/dyaml/test/inputoutput.d
deleted file mode 100644
index 758def8..0000000
--- a/src/ext_depends/D-YAML/source/dyaml/test/inputoutput.d
+++ /dev/null
@@ -1,92 +0,0 @@
-
-// Copyright Ferdinand Majerech 2011-2014.
-// 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)
-
-module dyaml.test.inputoutput;
-
-@safe unittest
-{
- import std.array : join, split;
- import std.conv : to;
- import std.exception : assertThrown;
- import std.file : readText;
- import std.system : endian, Endian;
-
- import dyaml : Loader, Node, YAMLException;
- import dyaml.test.common : run;
-
- /**
- Get an UTF-16 byte order mark.
-
- Params: wrong = Get the incorrect BOM for this system.
-
- Returns: UTF-16 byte order mark.
- */
- static wchar bom16(bool wrong = false) pure @safe
- {
- wchar little = '\uFEFF';
- wchar big = '\uFFFE';
- if (!wrong)
- {
- return endian == Endian.littleEndian ? little : big;
- }
- return endian == Endian.littleEndian ? big : little;
- }
- /**
- Get an UTF-32 byte order mark.
-
- Params: wrong = Get the incorrect BOM for this system.
-
- Returns: UTF-32 byte order mark.
- */
- static dchar bom32(bool wrong = false) pure @safe
- {
- dchar little = '\uFEFF';
- dchar big = '\uFFFE';
- if (!wrong)
- {
- return endian == Endian.littleEndian ? little : big;
- }
- return endian == Endian.littleEndian ? big : little;
- }
- /**
- Unicode input unittest. Tests various encodings.
-
- Params: unicodeFilename = File name to read from.
- */
- static void testUnicodeInput(string unicodeFilename) @safe
- {
- string data = readText(unicodeFilename);
- string expected = data.split().join(" ");
-
- Node output = Loader.fromString(data).load();
- assert(output.as!string == expected);
-
- foreach (buffer; [cast(ubyte[]) (bom16() ~ data.to!(wchar[])),
- cast(ubyte[]) (bom32() ~ data.to!(dchar[]))])
- {
- output = Loader.fromBuffer(buffer).load();
- assert(output.as!string == expected);
- }
- }
- /**
- Unicode input error unittest. Tests various encodings with incorrect BOMs.
-
- Params: unicodeFilename = File name to read from.
- */
- static void testUnicodeInputErrors(string unicodeFilename) @safe
- {
- string data = readText(unicodeFilename);
- foreach (buffer; [cast(ubyte[]) (data.to!(wchar[])),
- cast(ubyte[]) (data.to!(dchar[])),
- cast(ubyte[]) (bom16(true) ~ data.to!(wchar[])),
- cast(ubyte[]) (bom32(true) ~ data.to!(dchar[]))])
- {
- assertThrown(Loader.fromBuffer(buffer).load());
- }
- }
- run(&testUnicodeInput, ["unicode"]);
- run(&testUnicodeInputErrors, ["unicode"]);
-}
diff --git a/src/ext_depends/D-YAML/source/dyaml/test/reader.d b/src/ext_depends/D-YAML/source/dyaml/test/reader.d
deleted file mode 100644
index c20df6f..0000000
--- a/src/ext_depends/D-YAML/source/dyaml/test/reader.d
+++ /dev/null
@@ -1,37 +0,0 @@
-
-// 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)
-
-module dyaml.test.reader;
-
-@safe unittest
-{
- import std.exception :assertThrown;
-
- import dyaml.test.common : readData, run;
- import dyaml.reader : Reader, ReaderException;
-
- /**
- Try reading entire file through Reader, expecting an error (the file is invalid).
-
- Params: data = Stream to read.
- */
- static void runReader(ubyte[] fileData) @safe
- {
- auto reader = new Reader(fileData);
- while(reader.peek() != '\0') { reader.forward(); }
- }
-
- /**
- Stream error unittest. Tries to read invalid input files, expecting errors.
-
- Params: errorFilename = File name to read from.
- */
- static void testStreamError(string errorFilename) @safe
- {
- assertThrown!ReaderException(runReader(readData(errorFilename)));
- }
- run(&testStreamError, ["stream-error"]);
-}
diff --git a/src/ext_depends/D-YAML/source/dyaml/test/representer.d b/src/ext_depends/D-YAML/source/dyaml/test/representer.d
index 4a1ae67..eeac157 100644
--- a/src/ext_depends/D-YAML/source/dyaml/test/representer.d
+++ b/src/ext_depends/D-YAML/source/dyaml/test/representer.d
@@ -9,12 +9,12 @@ module dyaml.test.representer;
@safe unittest
{
import std.array : Appender, array;
+ import std.conv : text;
import std.meta : AliasSeq;
import std.path : baseName, stripExtension;
import std.utf : toUTF8;
import dyaml : dumper, Loader, Node;
- import dyaml.test.common : assertNodesEqual, run;
import dyaml.test.constructor : expected;
/**
@@ -38,13 +38,9 @@ module dyaml.test.representer;
auto loader = Loader.fromString(emitStream.data.toUTF8);
loader.name = "TEST";
- const readNodes = loader.array;
+ auto readNodes = loader.array;
- assert(expectedNodes.length == readNodes.length);
- foreach (n; 0 .. expectedNodes.length)
- {
- assertNodesEqual(expectedNodes[n], readNodes[n]);
- }
+ assert(expectedNodes == readNodes, text("Got '", readNodes, "', expected '", expectedNodes, "'"));
}
}
foreach (key, _; expected)
diff --git a/src/ext_depends/D-YAML/source/dyaml/test/resolver.d b/src/ext_depends/D-YAML/source/dyaml/test/resolver.d
deleted file mode 100644
index ea93720..0000000
--- a/src/ext_depends/D-YAML/source/dyaml/test/resolver.d
+++ /dev/null
@@ -1,39 +0,0 @@
-
-// 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)
-
-module dyaml.test.resolver;
-
-@safe unittest
-{
- import std.conv : text;
- import std.file : readText;
- import std.string : strip;
-
- import dyaml : Loader, Node, NodeID;
- import dyaml.test.common : run;
-
-
- /**
- Implicit tag resolution unittest.
-
- Params:
- dataFilename = File with unittest data.
- detectFilename = Dummy filename used to specify which data filenames to use.
- */
- static void testImplicitResolver(string dataFilename, string detectFilename) @safe
- {
- const correctTag = readText(detectFilename).strip();
-
- auto node = Loader.fromFile(dataFilename).load();
- assert(node.nodeID == NodeID.sequence, text("Expected sequence when reading '", dataFilename, "', got ", node.nodeID));
- foreach (Node scalar; node)
- {
- assert(scalar.nodeID == NodeID.scalar, text("Expected sequence of scalars when reading '", dataFilename, "', got sequence of ", scalar.nodeID));
- assert(scalar.tag == correctTag, text("Expected tag '", correctTag, "' when reading '", dataFilename, "', got '", scalar.tag, "'"));
- }
- }
- run(&testImplicitResolver, ["data", "detect"]);
-}
diff --git a/src/ext_depends/D-YAML/source/dyaml/test/suite.d b/src/ext_depends/D-YAML/source/dyaml/test/suite.d
new file mode 100644
index 0000000..2146c37
--- /dev/null
+++ b/src/ext_depends/D-YAML/source/dyaml/test/suite.d
@@ -0,0 +1,384 @@
+module dyaml.test.suite;
+
+import std.algorithm;
+import std.conv;
+import std.datetime.stopwatch;
+import std.exception;
+import std.file;
+import std.format;
+import std.meta;
+import std.path;
+import std.range;
+import std.stdio;
+import std.string;
+import std.typecons;
+import dyaml;
+import dyaml.event;
+import dyaml.parser;
+import dyaml.reader;
+import dyaml.scanner;
+import dyaml.test.suitehelpers;
+
+private version(unittest):
+
+debug(verbose)
+{
+ enum alwaysPrintTestResults = true;
+}
+else
+{
+ enum alwaysPrintTestResults = false;
+}
+
+struct TestResult {
+ string name;
+ Nullable!bool emitter;
+ Nullable!bool constructor;
+ Nullable!bool loaderError;
+ Nullable!bool mark1Error;
+ Nullable!bool mark2Error;
+ Nullable!bool implicitResolver;
+ Nullable!bool events;
+ Nullable!bool specificLoaderError;
+ Nullable!Mark mark1;
+ Nullable!Mark mark2;
+ Event[] parsedData;
+ Event[][2 * 2 * 5] parsedDataResult;
+ Node[] loadedData;
+ Exception nonDYAMLException;
+ MarkedYAMLException exception;
+ string eventsExpected;
+ string eventsGenerated;
+ string generatedLoadErrorMessage;
+ string expectedLoadErrorMessage;
+ string expectedTags;
+ string generatedTags;
+}
+
+/// Pretty-print the differences between two arrays
+auto prettyDifferencePrinter(alias eqPred = (a,b) => a == b, T)(string title, T[] expected, T[] got, bool trimWhitespace = false) @safe
+{
+ struct Result
+ {
+ void foo() {
+ toString(nullSink);
+ }
+ void toString(W)(ref W writer) const
+ {
+ import std.format : formattedWrite;
+ import std.range : put;
+ import std.string : lineSplitter;
+ size_t minWidth = 10;
+ foreach (line; chain(expected, got))
+ {
+ if (line.text.length + 1 > minWidth)
+ {
+ minWidth = line.text.length + 1;
+ }
+ }
+ void writeSideBySide(ubyte colour, string a, string b)
+ {
+ if (trimWhitespace)
+ {
+ a = strip(a);
+ b = strip(b);
+ }
+ writer.formattedWrite!"%s%-(%s%)%s"(colourPrinter(colour, a), " ".repeat(minWidth - a.length), colourPrinter(colour, b));
+ }
+ writefln!"%-(%s%)%s%-(%s%)"("=".repeat(max(0, minWidth * 2 - title.length) / 2), title, "=".repeat(max(0, minWidth * 2 - title.length) / 2));
+ writeSideBySide(0, "Expected", "Got");
+ put(writer, "\n");
+ foreach (line1, line2; zip(StoppingPolicy.longest, expected, got))
+ {
+ static if (is(T : const char[]))
+ {
+ if (trimWhitespace)
+ {
+ line1 = strip(line1);
+ line2 = strip(line2);
+ }
+ }
+ ubyte colour = (eqPred(line1, line2)) ? 32 : 31;
+ writeSideBySide(colour, line1.text, line2.text);
+ put(writer, "\n");
+ }
+ }
+ }
+ return Result();
+}
+
+/**
+Run a single test from the test suite.
+Params:
+ name = The filename of the document to load, containing the test data
+*/
+TestResult runTest(string name, Node doc) @safe
+{
+ TestResult result;
+ string[string] testData;
+ void tryLoadTestData(string what)
+ {
+ if (what in doc)
+ {
+ testData[what] = doc[what].as!string;
+ doc.removeAt(what);
+ }
+ }
+ string yamlPath(string testName, string section)
+ {
+ return format!"%s:%s"(testName, section);
+ }
+ tryLoadTestData("name");
+ result.name = name~"#"~testData.get("name", "UNNAMED");
+ Nullable!Mark getMark(string key)
+ {
+ if (auto node = key in doc)
+ {
+ Mark mark;
+ if ("name" in *node)
+ {
+ mark.name = (*node)["name"].as!string;
+ }
+ else // default to the test name
+ {
+ // if we ever have multiple yaml blocks to parse, be sure to change this
+ mark.name = yamlPath(result.name, "yaml");
+ }
+ if ("line" in *node)
+ {
+ mark.line = cast(ushort)((*node)["line"].as!ushort - 1);
+ }
+ if ("column" in *node)
+ {
+ mark.column = cast(ushort)((*node)["column"].as!ushort - 1);
+ }
+ return Nullable!Mark(mark);
+ }
+ return Nullable!Mark.init;
+ }
+ tryLoadTestData("tags");
+ tryLoadTestData("from");
+ tryLoadTestData("yaml");
+ tryLoadTestData("fail");
+ tryLoadTestData("json"); //not yet implemented
+ tryLoadTestData("dump"); //not yet implemented
+ tryLoadTestData("detect");
+ tryLoadTestData("tree");
+ tryLoadTestData("error");
+ tryLoadTestData("code");
+ assert("yaml" in testData);
+ {
+ result.expectedLoadErrorMessage = testData.get("error", "");
+ result.mark1 = getMark("mark");
+ result.mark2 = getMark("mark2");
+ try
+ {
+ result.parsedData = parseData(testData["yaml"], yamlPath(result.name, "yaml")).array;
+ result.loadedData = Loader.fromString(testData["yaml"], yamlPath(result.name, "yaml")).array;
+ result.emitter = testEmitterStyles(yamlPath(result.name, "canonical"), result.parsedData, result.parsedDataResult);
+ result.mark1Error = result.mark1.isNull;
+ result.mark2Error = result.mark2.isNull;
+ }
+ catch (MarkedYAMLException e)
+ {
+ result.exception = e;
+ result.generatedLoadErrorMessage = e.msg;
+ result.mark1Error = !result.mark1.isNull && (result.mark1.get() == e.mark);
+ result.mark2Error = result.mark2 == e.mark2;
+ if (testData.get("fail", "false") == "false")
+ {
+ result.loaderError = false;
+ }
+ else
+ {
+ result.loaderError = true;
+ }
+ }
+ catch (Exception e)
+ {
+ // all non-DYAML exceptions are failures.
+ result.nonDYAMLException = e;
+ result.generatedLoadErrorMessage = e.msg;
+ result.loaderError = false;
+ }
+ result.specificLoaderError = strip(result.generatedLoadErrorMessage) == strip(result.expectedLoadErrorMessage);
+ }
+ if (result.loaderError.get(false))
+ {
+ // skip other tests if loading failure was expected, because we don't
+ // have a way to run them yet
+ return result;
+ }
+ if ("tree" in testData)
+ {
+ result.eventsGenerated = result.parsedData.map!(x => strip(x.text)).join("\n");
+ result.eventsExpected = testData["tree"].lineSplitter.map!(x => strip(x)).join("\n");
+ result.events = result.eventsGenerated == result.eventsExpected;
+ }
+ if ("code" in testData)
+ {
+ result.constructor = testConstructor(testData["yaml"], testData["code"]);
+ }
+ if ("detect" in testData)
+ {
+ result.implicitResolver = testImplicitResolver(yamlPath(result.name, "yaml"), testData["yaml"], testData["detect"], result.generatedTags, result.expectedTags);
+ }
+ foreach (string remaining, Node _; doc)
+ {
+ writeln("Warning: Unhandled section '", remaining, "' in ", result.name);
+ }
+ return result;
+}
+
+enum goodColour = 32;
+enum badColour = 31;
+/**
+Print something to the console in colour.
+Params:
+ colour = The id of the colour to print, using the 256-colour palette
+ data = Something to print
+*/
+private auto colourPrinter(T)(ubyte colour, T data) @safe pure
+{
+ struct Printer
+ {
+ void toString(S)(ref S sink)
+ {
+ sink.formattedWrite!"\033[%s;1m%s\033[0m"(colour, data);
+ }
+ }
+ return Printer();
+}
+
+/**
+Run all tests in the test suite and print relevant results. The test docs are
+all found in the ./test/data dir.
+*/
+bool runTests()
+{
+ auto stopWatch = StopWatch(AutoStart.yes);
+ bool failed;
+ uint testsRun, testSetsRun, testsFailed;
+ foreach (string name; dirEntries(buildNormalizedPath("test"), "*.yaml", SpanMode.depth)/*.chain(dirEntries(buildNormalizedPath("yaml-test-suite/src"), "*.yaml", SpanMode.depth))*/)
+ {
+ Node doc;
+ try
+ {
+ doc = Loader.fromFile(name).load();
+ }
+ catch (Exception e)
+ {
+ writefln!"[%s] %s"(colourPrinter(badColour, "FAIL"), name);
+ writeln(colourPrinter(badColour, e));
+ assert(0, "Could not load test doc '"~name~"', bailing");
+ }
+ assert (doc.nodeID == NodeID.sequence, name~"'s root node is not a sequence!");
+ foreach (Node test; doc)
+ {
+ testSetsRun++;
+ bool resultPrinted;
+ // make sure the paths are normalized on windows by replacing backslashes with slashes
+ TestResult result = runTest(name.replace("\\", "/"), test);
+ void printResult(string label, Nullable!bool value)
+ {
+ if (!value.isNull)
+ {
+ if (!value.get)
+ {
+ testsFailed++;
+ }
+ testsRun++;
+ }
+ if (alwaysPrintTestResults && value.get(false))
+ {
+ resultPrinted = true;
+ writef!"[%s]"(colourPrinter(goodColour, label));
+ }
+ else if (!value.get(true))
+ {
+ resultPrinted = true;
+ failed = true;
+ writef!"[%s]"(colourPrinter(badColour, label));
+ }
+ }
+ printResult("Emitter", result.emitter);
+ printResult("Constructor", result.constructor);
+ printResult("Mark", result.mark1Error);
+ printResult("Context mark", result.mark2Error);
+ printResult("LoaderError", result.loaderError);
+ printResult("Resolver", result.implicitResolver);
+ printResult("Events", result.events);
+ printResult("SpecificLoaderError", result.specificLoaderError);
+ if (resultPrinted)
+ {
+ writeln(" ", result.name);
+ }
+ if (!result.loaderError.get(true))
+ {
+ if (result.exception is null && result.nonDYAMLException is null)
+ {
+ writeln("\tNo Exception thrown");
+ }
+ else if (result.nonDYAMLException !is null)
+ {
+ writeln(result.nonDYAMLException);
+ }
+ else if (result.exception !is null)
+ {
+ writeln(result.exception);
+ }
+ }
+ else
+ {
+ if (!result.mark1Error.get(true))
+ {
+ writeln(prettyDifferencePrinter("Mark mismatch", [result.mark1.text], [result.exception.mark.text]));
+ }
+ if (!result.mark2Error.get(true))
+ {
+ writeln(prettyDifferencePrinter("Context mark mismatch", [result.mark2.text], [result.exception.mark2.text]));
+ }
+ }
+ if (!result.emitter.get(true))
+ {
+ enum titles = [ "Normal", "Canonical" ];
+ enum styleTitles =
+ [
+ "Block literal", "Block folded", "Block double-quoted", "Block single-quoted", "Block plain",
+ "Flow literal", "Flow folded", "Flow double-quoted", "Flow single-quoted", "Flow plain",
+ "Block literal", "Block folded", "Block double-quoted", "Block single-quoted", "Block plain",
+ "Flow literal", "Flow folded", "Flow double-quoted", "Flow single-quoted", "Flow plain",
+ ];
+ foreach (idx, parsed; result.parsedDataResult)
+ {
+ writeln(prettyDifferencePrinter!eventCompare(styleTitles[idx], result.parsedData, parsed));
+ }
+ }
+ if (!result.events.get(true))
+ {
+ writeln(prettyDifferencePrinter("Events", result.eventsExpected.splitLines, result.eventsGenerated.splitLines, true));
+ }
+ if (!result.specificLoaderError.get(true))
+ {
+ writeln(prettyDifferencePrinter("Expected error", result.expectedLoadErrorMessage.splitLines, result.generatedLoadErrorMessage.splitLines));
+ }
+ if (!result.implicitResolver.get(true))
+ {
+ writeln(prettyDifferencePrinter("Expected error", result.expectedTags.splitLines, result.generatedTags.splitLines));
+ }
+ }
+ }
+ if (alwaysPrintTestResults || failed)
+ {
+ if (testsFailed > 0)
+ {
+ writeln(colourPrinter(badColour, "tests failed: "), testsFailed);
+ }
+ writeln(testSetsRun, " test sets (", testsRun, " tests total) completed successfully in ", stopWatch.peek());
+ }
+ return failed;
+}
+
+unittest {
+ assert(!runTests());
+}
diff --git a/src/ext_depends/D-YAML/source/dyaml/test/suitehelpers.d b/src/ext_depends/D-YAML/source/dyaml/test/suitehelpers.d
new file mode 100644
index 0000000..3016843
--- /dev/null
+++ b/src/ext_depends/D-YAML/source/dyaml/test/suitehelpers.d
@@ -0,0 +1,224 @@
+
+// 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)
+
+module dyaml.test.suitehelpers;
+
+import dyaml;
+import dyaml.emitter;
+import dyaml.event;
+import dyaml.parser;
+import dyaml.reader;
+import dyaml.scanner;
+import dyaml.token;
+import dyaml.test.constructor;
+
+import std.algorithm;
+import std.array;
+import std.conv;
+import std.exception;
+import std.file;
+import std.range;
+import std.string;
+
+package version(unittest):
+
+// Like other types, wchar and dchar use the system's endianness, so \uFEFF
+// will always be the 'correct' BOM and '\uFFFE' will always be the 'wrong' one
+enum wchar[] bom16 = ['\uFEFF', '\uFFFE'];
+enum dchar[] bom32 = ['\uFEFF', '\uFFFE'];
+
+Parser parseData(string data, string name = "TEST") @safe
+{
+ auto reader = Reader(cast(ubyte[])data.dup, name);
+ auto scanner = Scanner(reader);
+ return new Parser(scanner);
+}
+
+/**
+Test scanner by scanning a document, expecting no errors.
+
+Params:
+ name = Name of the document being scanned
+ data = Data to scan.
+*/
+void testScanner(string name, string data) @safe
+{
+ ubyte[] yamlData = cast(ubyte[])data.dup;
+ string[] tokens;
+ foreach (token; Scanner(Reader(yamlData, name)))
+ {
+ tokens ~= token.id.text;
+ }
+}
+
+/**
+Implicit tag resolution unittest.
+
+Params:
+ name = Name of the document being tested
+ data = Document to compare
+ detectData = The tag that each scalar should resolve to
+*/
+bool testImplicitResolver(string name, string data, string detectData, out string generatedTags, out string expectedTags) @safe
+{
+ const correctTag = detectData.strip();
+
+ const node = Loader.fromString(data, name).load();
+ if (node.nodeID != NodeID.sequence)
+ {
+ return false;
+ }
+ bool success = true;
+ foreach (const Node scalar; node)
+ {
+ generatedTags ~= scalar.tag ~ "\n";
+ expectedTags ~= correctTag ~ "\n";
+ if ((scalar.nodeID != NodeID.scalar) || (scalar.tag != correctTag))
+ {
+ success = false;
+ }
+ }
+ return success;
+}
+
+// Try to emit an event range.
+Event[] emitTestCommon(string name, Event[] events, bool canonical) @safe
+{
+ auto emitStream = new Appender!string();
+ auto emitter = Emitter!(typeof(emitStream), char)(emitStream, canonical, 2, 80, LineBreak.unix);
+ foreach (event; events)
+ {
+ emitter.emit(event);
+ }
+ return parseData(emitStream.data, name).array;
+}
+/**
+Test emitter by checking if events remain equal after round-tripping, with and
+without canonical output enabled.
+
+Params:
+ name = Name of the document being tested
+ events = Events to test
+ results = Events that were produced by round-tripping
+*/
+bool testEmitter(string name, Event[] events, out Event[][2] results) @safe
+{
+ bool matching = true;
+ foreach (idx, canonicalOutput; [false, true])
+ {
+ results[idx] = emitTestCommon(name, events, canonicalOutput);
+
+ if (!equal!eventCompare(events, results[idx]))
+ {
+ matching = false;
+ }
+ }
+ return matching;
+}
+/**
+Test emitter by checking if events remain equal after round-tripping, with all
+combinations of styles.
+
+Params:
+ name = Name of the document being tested
+ events = Events to test
+ results = Events that were produced by round-tripping
+*/
+bool testEmitterStyles(string name, Event[] events, out Event[][2 * 2 * 5] results) @safe
+{
+ size_t idx;
+ foreach (styles; cartesianProduct(
+ [CollectionStyle.block, CollectionStyle.flow],
+ [ScalarStyle.literal, ScalarStyle.folded,
+ ScalarStyle.doubleQuoted, ScalarStyle.singleQuoted,
+ ScalarStyle.plain],
+ [false, true]))
+ {
+ const collectionStyle = styles[0];
+ const scalarStyle = styles[1];
+ const canonical = styles[2];
+ Event[] styledEvents;
+ foreach (event; events)
+ {
+ if (event.id == EventID.scalar)
+ {
+ event = scalarEvent(Mark(), Mark(), event.anchor, event.tag,
+ event.implicit,
+ event.value, scalarStyle);
+ }
+ else if (event.id == EventID.sequenceStart)
+ {
+ event = sequenceStartEvent(Mark(), Mark(), event.anchor,
+ event.tag, event.implicit, collectionStyle);
+ }
+ else if (event.id == EventID.mappingStart)
+ {
+ event = mappingStartEvent(Mark(), Mark(), event.anchor,
+ event.tag, event.implicit, collectionStyle);
+ }
+ styledEvents ~= event;
+ }
+ auto newEvents = emitTestCommon(name, styledEvents, canonical);
+ results[idx++] = newEvents;
+ if (!equal!eventCompare(events, newEvents))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+Constructor unittest.
+
+Params:
+ data = The document being tested
+ base = A unique id corresponding to one of the premade sequences in dyaml.test.constructor
+*/
+bool testConstructor(string data, string base) @safe
+{
+ assert((base in expected) !is null, "Unimplemented constructor test: " ~ base);
+ auto loader = Loader.fromString(data);
+
+ Node[] exp = expected[base];
+
+ //Compare with expected results document by document.
+ return equal(loader, exp);
+}
+
+bool eventCompare(const Event a, const Event b) @safe pure
+{
+ //Different event types.
+ if (a.id != b.id)
+ {
+ return false;
+ }
+ //Different anchor (if applicable).
+ if (a.id.among!(EventID.sequenceStart, EventID.mappingStart, EventID.alias_, EventID.scalar)
+ && a.anchor != b.anchor)
+ {
+ return false;
+ }
+ //Different collection tag (if applicable).
+ if (a.id.among!(EventID.sequenceStart, EventID.mappingStart) && a.tag != b.tag)
+ {
+ return false;
+ }
+ if (a.id == EventID.scalar)
+ {
+ //Different scalar tag (if applicable).
+ if (!(a.implicit || b.implicit) && a.tag != b.tag)
+ {
+ return false;
+ }
+ //Different scalar value.
+ if (a.value != b.value)
+ {
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/src/ext_depends/D-YAML/source/dyaml/test/tokens.d b/src/ext_depends/D-YAML/source/dyaml/test/tokens.d
deleted file mode 100644
index d3dce6e..0000000
--- a/src/ext_depends/D-YAML/source/dyaml/test/tokens.d
+++ /dev/null
@@ -1,93 +0,0 @@
-
-// 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)
-
-module dyaml.test.tokens;
-
-@safe unittest
-{
- import std.array : split;
- import std.conv : text;
- import std.file : readText;
-
- import dyaml.test.common : run;
- import dyaml.reader : Reader;
- import dyaml.scanner : Scanner;
- import dyaml.token : TokenID;
-
- // Read and scan a YAML doc, returning a range of tokens.
- static auto scanTestCommon(string filename) @safe
- {
- ubyte[] yamlData = cast(ubyte[])readText(filename).dup;
- return Scanner(new Reader(yamlData, filename));
- }
-
- /**
- Test tokens output by scanner.
-
- Params:
- dataFilename = File to scan.
- tokensFilename = File containing expected tokens.
- */
- static void testTokens(string dataFilename, string tokensFilename) @safe
- {
- //representations of YAML tokens in tokens file.
- auto replace = [
- TokenID.directive: "%",
- TokenID.documentStart: "---",
- TokenID.documentEnd: "...",
- TokenID.alias_: "*",
- TokenID.anchor: "&",
- TokenID.tag: "!",
- TokenID.scalar: "_",
- TokenID.blockSequenceStart: "[[",
- TokenID.blockMappingStart: "{{",
- TokenID.blockEnd: "]}",
- TokenID.flowSequenceStart: "[",
- TokenID.flowSequenceEnd: "]",
- TokenID.flowMappingStart: "{",
- TokenID.flowMappingEnd: "}",
- TokenID.blockEntry: ",",
- TokenID.flowEntry: ",",
- TokenID.key: "?",
- TokenID.value: ":"
- ];
-
- string[] tokens;
- string[] expectedTokens = readText(tokensFilename).split();
-
- foreach (token; scanTestCommon(dataFilename))
- {
- if (token.id != TokenID.streamStart && token.id != TokenID.streamEnd)
- {
- tokens ~= replace[token.id];
- }
- }
-
- assert(tokens == expectedTokens,
- text("In token test for '", tokensFilename, "', expected '", expectedTokens, "', got '", tokens, "'"));
- }
-
- /**
- Test scanner by scanning a file, expecting no errors.
-
- Params:
- dataFilename = File to scan.
- canonicalFilename = Another file to scan, in canonical YAML format.
- */
- static void testScanner(string dataFilename, string canonicalFilename) @safe
- {
- foreach (filename; [dataFilename, canonicalFilename])
- {
- string[] tokens;
- foreach (token; scanTestCommon(filename))
- {
- tokens ~= token.id.text;
- }
- }
- }
- run(&testTokens, ["data", "tokens"]);
- run(&testScanner, ["data", "canonical"]);
-}
diff --git a/src/ext_depends/D-YAML/source/dyaml/token.d b/src/ext_depends/D-YAML/source/dyaml/token.d
index 5400a3f..b67400f 100644
--- a/src/ext_depends/D-YAML/source/dyaml/token.d
+++ b/src/ext_depends/D-YAML/source/dyaml/token.d
@@ -62,8 +62,6 @@ enum DirectiveType : ubyte
/// 32 bytes on 64-bit.
struct Token
{
- @disable int opCmp(ref Token);
-
// 16B
/// Value of the token, if any.
///