Merge pull request #54 from BBasile/master

Merge changes from the fork used by the dlang tour
This commit is contained in:
Petar Kirov 2017-05-16 22:09:23 +03:00 committed by GitHub
commit 8e1e9893d1
16 changed files with 192 additions and 107 deletions

14
.editorconfig Normal file
View file

@ -0,0 +1,14 @@
# EditorConfig file: http://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
end_of_line = lf
insert_final_newline = true
[*.d]
charset = utf-8
indent_style = space
indent_size = 4
trim_trailing_whitespace = true

View file

@ -2,9 +2,13 @@
D:YAML 0.5 D:YAML 0.5
========== ==========
.. image:: https://travis-ci.org/kiith-sa/D-YAML.svg?branch=master This is a fork of the D:YAML library by kiith-sa (https://github.com/kiith-sa/D-YAML).
.. image:: https://raw.githubusercontent.com/kiith-sa/D-YAML/master/code.dlang.org-shield.png The intent of this fork is to provide a version suitable for use by dlang-tour, until
:target: http://code.dlang.org fixes are accepted upstream.
.. image:: https://travis-ci.org/dlang-tour/dyaml-dlang-tour.svg?branch=master
.. image:: https://img.shields.io/dub/v/dyaml-dlang-tour.svg
:target: http://code.dlang.org/packages/dyaml-dlang-tour
**Note**: D:YAML 0.5 brings some **breaking changes**. See the **Note**: D:YAML 0.5 brings some **breaking changes**. See the
`changelog <https://github.com/kiith-sa/D-YAML/blob/master/CHANGES.rst>`_. `changelog <https://github.com/kiith-sa/D-YAML/blob/master/CHANGES.rst>`_.

View file

@ -1,5 +1,5 @@
{ {
"name": "dyaml", "name": "dyaml-dlang-tour",
"description": "YAML parser and emitter", "description": "YAML parser and emitter",
"authors": [ "Ferdinand Majerech" ], "authors": [ "Ferdinand Majerech" ],
"libs": [], "libs": [],

View file

@ -110,7 +110,7 @@ final class Composer
{ {
//Get the root node of the next document. //Get the root node of the next document.
assert(!parser_.checkEvent(EventID.StreamEnd), assert(!parser_.checkEvent(EventID.StreamEnd),
"Trying to get a node from Composer when there is no node to " "Trying to get a node from Composer when there is no node to " ~
"get. use checkNode() to determine if there is a node."); "get. use checkNode() to determine if there is a node.");
return composeDocument(); return composeDocument();
@ -120,14 +120,14 @@ final class Composer
Node getSingleNode() @trusted Node getSingleNode() @trusted
{ {
assert(!parser_.checkEvent(EventID.StreamEnd), assert(!parser_.checkEvent(EventID.StreamEnd),
"Trying to get a node from Composer when there is no node to " "Trying to get a node from Composer when there is no node to " ~
"get. use checkNode() to determine if there is a node."); "get. use checkNode() to determine if there is a node.");
Node document = composeDocument(); Node document = composeDocument();
//Ensure that the stream contains no more documents. //Ensure that the stream contains no more documents.
enforce(parser_.checkEvent(EventID.StreamEnd), enforce(parser_.checkEvent(EventID.StreamEnd),
new ComposerException("Expected single document in the stream, " new ComposerException("Expected single document in the stream, " ~
"but found another document.", "but found another document.",
parser_.getEvent().startMark)); parser_.getEvent().startMark));
@ -292,11 +292,11 @@ final class Composer
void error(Node node) void error(Node node)
{ {
//this is Composer, but the code is related to Constructor. //this is Composer, but the code is related to Constructor.
throw new ConstructorException("While constructing a mapping, " throw new ConstructorException("While constructing a mapping, " ~
"expected a mapping or a list of " "expected a mapping or a list of " ~
"mappings for merging, but found: " "mappings for merging, but found: " ~
~ node.type.toString() ~ node.type.toString() ~
" NOTE: line/column shows topmost parent " " NOTE: line/column shows topmost parent " ~
"to which the content is being merged", "to which the content is being merged",
startMark, endMark); startMark, endMark);
} }

View file

@ -352,7 +352,7 @@ final class Constructor
assert((tag in fromScalar_) is null && assert((tag in fromScalar_) is null &&
(tag in fromSequence_) is null && (tag in fromSequence_) is null &&
(tag in fromMapping_) is null, (tag in fromMapping_) is null,
"Constructor function for tag " ~ tag.get ~ " is already " "Constructor function for tag " ~ tag.get ~ " is already " ~
"specified. Can't specify another one."); "specified. Can't specify another one.");

View file

@ -297,7 +297,7 @@ struct Dumper
foreach(handle, prefix; tags) foreach(handle, prefix; tags)
{ {
assert(handle.length >= 1 && handle[0] == '!' && handle[$ - 1] == '!', assert(handle.length >= 1 && handle[0] == '!' && handle[$ - 1] == '!',
"A tag handle is empty or does not start and end with a " "A tag handle is empty or does not start and end with a " ~
"'!' character : " ~ handle); "'!' character : " ~ handle);
assert(prefix.length >= 1, "A tag prefix is empty"); assert(prefix.length >= 1, "A tag prefix is empty");
t ~= TagDirective(handle, prefix); t ~= TagDirective(handle, prefix);

View file

@ -54,7 +54,7 @@ class EmitterException : YAMLException
private alias EmitterException Error; private alias EmitterException Error;
//Stores results of analysis of a scalar, determining e.g. what scalar style to use. //Stores results of analysis of a scalar, determining e.g. what scalar style to use.
align(4) struct ScalarAnalysis struct ScalarAnalysis
{ {
//Scalar itself. //Scalar itself.
string scalar; string scalar;
@ -228,7 +228,7 @@ struct Emitter
int popIndent() @trusted int popIndent() @trusted
{ {
enforce(indents_.length > 0, enforce(indents_.length > 0,
new YAMLException("Emitter: Need to pop an indent level but there" new YAMLException("Emitter: Need to pop an indent level but there" ~
" are no indent levels left")); " are no indent levels left"));
const result = indents_.back; const result = indents_.back;
indents_.length = indents_.length - 1; indents_.length = indents_.length - 1;
@ -490,7 +490,7 @@ struct Emitter
} }
break; break;
default: default:
throw new Error("Expected Alias, Scalar, SequenceStart or " throw new Error("Expected Alias, Scalar, SequenceStart or " ~
"MappingStart, but got: " ~ event_.idString); "MappingStart, but got: " ~ event_.idString);
} }
} }

View file

@ -227,8 +227,12 @@ Event scalarEvent(const Mark start, const Mark end, const Anchor anchor, const T
result.value = value; result.value = value;
result.startMark = start; result.startMark = start;
result.endMark = end; result.endMark = end;
() @trusted {
result.anchor = anchor; result.anchor = anchor;
result.tag = tag; result.tag = tag;
}();
result.id = EventID.Scalar; result.id = EventID.Scalar;
result.scalarStyle = style; result.scalarStyle = style;
result.implicit = implicit[0]; result.implicit = implicit[0];

View file

@ -52,17 +52,17 @@ struct Flags(names ...) if(names.length <= 8)
foreach(index, name; names) foreach(index, name; names)
{ {
string istr = to!string(index); string istr = to!string(index);
result ~= "\n" result ~= "\n" ~
"@property bool " ~ name ~ "(bool value) pure @safe nothrow\n" "@property bool " ~ name ~ "(bool value) pure @safe nothrow\n" ~
"{\n" "{\n" ~
" flags_ = value ? flags_ | (1 <<" ~ istr ~ ")\n" " flags_ = value ? flags_ | (1 <<" ~ istr ~ ")\n" ~
" : flags_ & (0xFF ^ (1 << " ~ istr ~"));\n" " : flags_ & (0xFF ^ (1 << " ~ istr ~"));\n" ~
" return value;\n" " return value;\n" ~
"}\n" "}\n" ~
"\n" "\n" ~
"@property bool " ~ name ~ "() const pure @safe nothrow\n" "@property bool " ~ name ~ "() const pure @safe nothrow\n" ~
"{\n" "{\n" ~
" return (flags_ >> " ~ istr ~ ") & 1;\n" " return (flags_ >> " ~ istr ~ ") & 1;\n" ~
"}\n"; "}\n";
} }
return result; return result;

View file

@ -179,7 +179,7 @@ struct Loader
* *
* Throws: YAMLException if yamlData contains data illegal in YAML. * Throws: YAMLException if yamlData contains data illegal in YAML.
*/ */
this(void[] yamlData) @safe this(void[] yamlData) @trusted
{ {
try try
{ {
@ -380,9 +380,9 @@ struct Loader
unittest unittest
{ {
char[] yaml_input = "red: '#ff0000'\n" char[] yaml_input = ("red: '#ff0000'\n" ~
"green: '#00ff00'\n" "green: '#00ff00'\n" ~
"blue: '#0000ff'".dup; "blue: '#0000ff'").dup;
auto colors = Loader.fromString(yaml_input).load(); auto colors = Loader.fromString(yaml_input).load();

View file

@ -454,7 +454,7 @@ struct Node
in in
{ {
assert(keys.length == values.length, assert(keys.length == values.length,
"Lengths of keys and values arrays to construct " "Lengths of keys and values arrays to construct " ~
"a YAML node from don't match"); "a YAML node from don't match");
} }
body body
@ -1631,6 +1631,7 @@ struct Node
return (cast(nothrowType)&value_.type)(); return (cast(nothrowType)&value_.type)();
} }
public:
// Determine if the value stored by the node is of specified type. // Determine if the value stored by the node is of specified type.
// //
// This only works for default YAML types, not for user defined types. // This only works for default YAML types, not for user defined types.
@ -1639,7 +1640,6 @@ struct Node
return this.type is typeid(Unqual!T); return this.type is typeid(Unqual!T);
} }
private:
// Is the value a bool? // Is the value a bool?
alias isType!bool isBool; alias isType!bool isBool;
@ -1685,6 +1685,7 @@ struct Node
else {return false;} else {return false;}
} }
private:
// Implementation of contains() and containsKey(). // Implementation of contains() and containsKey().
bool contains_(T, Flag!"key" key, string func)(T rhs) const @trusted bool contains_(T, Flag!"key" key, string func)(T rhs) const @trusted
{ {

View file

@ -99,7 +99,7 @@ final class Reader
auto endianResult = fixUTFByteOrder(buffer); auto endianResult = fixUTFByteOrder(buffer);
if(endianResult.bytesStripped > 0) if(endianResult.bytesStripped > 0)
{ {
throw new ReaderException("Size of UTF-16 or UTF-32 input not aligned " 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");
} }

View file

@ -226,33 +226,33 @@ final class Resolver
void addImplicitResolvers() @safe void addImplicitResolvers() @safe
{ {
addImplicitResolver("tag:yaml.org,2002:bool", addImplicitResolver("tag:yaml.org,2002:bool",
regex(r"^(?:yes|Yes|YES|no|No|NO|true|True|TRUE" regex(r"^(?:yes|Yes|YES|no|No|NO|true|True|TRUE" ~
"|false|False|FALSE|on|On|ON|off|Off|OFF)$"), "|false|False|FALSE|on|On|ON|off|Off|OFF)$"),
"yYnNtTfFoO"); "yYnNtTfFoO");
addImplicitResolver("tag:yaml.org,2002:float", addImplicitResolver("tag:yaml.org,2002:float",
regex(r"^(?:[-+]?([0-9][0-9_]*)\\.[0-9_]*" regex(r"^(?:[-+]?([0-9][0-9_]*)\\.[0-9_]*" ~
"(?:[eE][-+][0-9]+)?|[-+]?(?:[0-9][0-9_]" "(?:[eE][-+][0-9]+)?|[-+]?(?:[0-9][0-9_]" ~
"*)?\\.[0-9_]+(?:[eE][-+][0-9]+)?|[-+]?" "*)?\\.[0-9_]+(?:[eE][-+][0-9]+)?|[-+]?" ~
"[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]" "[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]" ~
"*|[-+]?\\.(?:inf|Inf|INF)|\\." "*|[-+]?\\.(?:inf|Inf|INF)|\\." ~
"(?:nan|NaN|NAN))$"), "(?:nan|NaN|NAN))$"),
"-+0123456789."); "-+0123456789.");
addImplicitResolver("tag:yaml.org,2002:int", addImplicitResolver("tag:yaml.org,2002:int",
regex(r"^(?:[-+]?0b[0-1_]+" regex(r"^(?:[-+]?0b[0-1_]+" ~
"|[-+]?0[0-7_]+" "|[-+]?0[0-7_]+" ~
"|[-+]?(?:0|[1-9][0-9_]*)" "|[-+]?(?:0|[1-9][0-9_]*)" ~
"|[-+]?0x[0-9a-fA-F_]+" "|[-+]?0x[0-9a-fA-F_]+" ~
"|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$"), "|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$"),
"-+0123456789"); "-+0123456789");
addImplicitResolver("tag:yaml.org,2002:merge", regex(r"^<<$"), "<"); addImplicitResolver("tag:yaml.org,2002:merge", regex(r"^<<$"), "<");
addImplicitResolver("tag:yaml.org,2002:null", addImplicitResolver("tag:yaml.org,2002:null",
regex(r"^$|^(?:~|null|Null|NULL)$"), "~nN\0"); regex(r"^$|^(?:~|null|Null|NULL)$"), "~nN\0");
addImplicitResolver("tag:yaml.org,2002:timestamp", addImplicitResolver("tag:yaml.org,2002:timestamp",
regex(r"^[0-9][0-9][0-9][0-9]-[0-9][0-9]-" regex(r"^[0-9][0-9][0-9][0-9]-[0-9][0-9]-" ~
"[0-9][0-9]|[0-9][0-9][0-9][0-9]-[0-9]" "[0-9][0-9]|[0-9][0-9][0-9][0-9]-[0-9]" ~
"[0-9]?-[0-9][0-9]?[Tt]|[ \t]+[0-9]" "[0-9]?-[0-9][0-9]?[Tt]|[ \t]+[0-9]" ~
"[0-9]?:[0-9][0-9]:[0-9][0-9]" "[0-9]?:[0-9][0-9]:[0-9][0-9]" ~
"(?:\\.[0-9]*)?(?:[ \t]*Z|[-+][0-9]" "(?:\\.[0-9]*)?(?:[ \t]*Z|[-+][0-9]" ~
"[0-9]?(?::[0-9][0-9])?)?$"), "[0-9]?(?::[0-9][0-9])?)?$"),
"0123456789"); "0123456789");
addImplicitResolver("tag:yaml.org,2002:value", regex(r"^=$"), "="); addImplicitResolver("tag:yaml.org,2002:value", regex(r"^=$"), "=");

View file

@ -320,7 +320,7 @@ final class Scanner
default: if(checkPlain()) { return fetchPlain(); } default: if(checkPlain()) { return fetchPlain(); }
} }
throw new ScannerException("While scanning for the next token, found character " throw new ScannerException("While scanning for the next token, found character " ~
"\'%s\', index %s that cannot start any token" "\'%s\', index %s that cannot start any token"
.format(c, to!int(c)), reader_.mark); .format(c, to!int(c)), reader_.mark);
} }
@ -368,7 +368,7 @@ final class Scanner
{ {
// Check if a simple key is required at the current position. // Check if a simple key is required at the current position.
const required = (flowLevel_ == 0 && indent_ == reader_.column); const required = (flowLevel_ == 0 && indent_ == reader_.column);
assert(allowSimpleKey_ || !required, "A simple key is required only if it is " assert(allowSimpleKey_ || !required, "A simple key is required only if it is " ~
"the first token in the current line. Therefore it is always allowed."); "the first token in the current line. Therefore it is always allowed.");
if(!allowSimpleKey_) { return; } if(!allowSimpleKey_) { return; }
@ -1606,7 +1606,7 @@ final class Scanner
if(overflow) if(overflow)
{ {
error("While scanning a double quoted scalar", startMark, error("While scanning a double quoted scalar", startMark,
"overflow when parsing an escape sequence of " "overflow when parsing an escape sequence of " ~
"hexadecimal numbers.", reader_.mark); "hexadecimal numbers.", reader_.mark);
return; return;
} }
@ -1773,7 +1773,7 @@ final class Scanner
reader_.sliceBuilder.finish(); reader_.sliceBuilder.finish();
reader_.forward(length); reader_.forward(length);
error("While scanning a plain scalar", startMark, error("While scanning a plain scalar", startMark,
"found unexpected ':' . Please check " "found unexpected ':' . Please check " ~
"http://pyyaml.org/wiki/YAMLColonInFlowContext for details.", "http://pyyaml.org/wiki/YAMLColonInFlowContext for details.",
reader_.mark); reader_.mark);
return Token.init; return Token.init;
@ -2015,7 +2015,7 @@ final class Scanner
const dchar c = reader_.peek(k); const dchar c = reader_.peek(k);
if(!c.isHexDigit) if(!c.isHexDigit)
{ {
auto msg = expected("URI escape sequence of 2 hexadecimal " auto msg = expected("URI escape sequence of 2 hexadecimal " ~
"numbers", c); "numbers", c);
error(contextMsg, startMark, msg, reader_.mark); error(contextMsg, startMark, msg, reader_.mark);
return; return;

View file

@ -1,6 +1,7 @@
module dyaml.stream; module dyaml.stream;
enum BOM { enum BOM
{
UTF8, /// UTF-8 UTF8, /// UTF-8
UTF16LE, /// UTF-16 Little Endian UTF16LE, /// UTF-16 Little Endian
UTF16BE, /// UTF-16 Big Endian UTF16BE, /// UTF-16 Big Endian
@ -12,63 +13,124 @@ import std.system;
private enum int NBOMS = 5; private enum int NBOMS = 5;
immutable Endian[NBOMS] BOMEndian = immutable Endian[NBOMS] BOMEndian =
[ std.system.endian, [
std.system.endian,
Endian.littleEndian, Endian.bigEndian, Endian.littleEndian, Endian.bigEndian,
Endian.littleEndian, Endian.bigEndian Endian.littleEndian, Endian.bigEndian
]; ];
immutable ubyte[][NBOMS] ByteOrderMarks = immutable ubyte[][NBOMS] ByteOrderMarks =
[ [0xEF, 0xBB, 0xBF], [
[0xEF, 0xBB, 0xBF],
[0xFF, 0xFE], [0xFF, 0xFE],
[0xFE, 0xFF], [0xFE, 0xFF],
[0xFF, 0xFE, 0x00, 0x00], [0xFF, 0xFE, 0x00, 0x00],
[0x00, 0x00, 0xFE, 0xFF] [0x00, 0x00, 0xFE, 0xFF]
]; ];
interface YStream { interface YStream
{
void writeExact(const void* buffer, size_t size); void writeExact(const void* buffer, size_t size);
size_t write(const(ubyte)[] buffer); size_t write(const(ubyte)[] buffer);
size_t write(const(char)[] str);
void flush(); void flush();
@property bool writeable(); @property bool writeable();
} }
class YMemoryStream : YStream { class YMemoryStream : YStream
{
ubyte[] data; ubyte[] data;
void writeExact(const void* buffer, size_t size) { void writeExact(const void* buffer, size_t size)
{
data ~= cast(ubyte[])buffer[0 .. size]; data ~= cast(ubyte[])buffer[0 .. size];
} }
size_t write(const(ubyte)[] buffer) { size_t write(const(ubyte)[] buffer)
{
data ~= buffer; data ~= buffer;
return buffer.length; return buffer.length;
} }
size_t write(const(char)[] str)
{
return write(cast(const(ubyte)[])str);
}
void flush() {} void flush() {}
@property bool writeable() { return true; } @property bool writeable() { return true; }
} }
class YFile : YStream { class YFile : YStream
{
static import std.stdio; static import std.stdio;
std.stdio.File file; std.stdio.File file;
this(string fn) { this(string fn)
{
this.file = std.stdio.File(fn, "w"); this.file = std.stdio.File(fn, "w");
} }
void writeExact(const void* buffer, size_t size) { this(std.stdio.File file)
this.file.write(cast(const ubyte[])buffer[0 .. size]); {
this.file = file;
} }
size_t write(const(ubyte)[] buffer) { unittest
this.file.write(buffer); {
import std.stdio : stdout;
auto stream = new YFile(stdout);
stream.write("Test writing to stdout through YFile stream\n");
}
void writeExact(const void* buffer, size_t size)
{
this.file.rawWrite(cast(const) buffer[0 .. size]);
}
size_t write(const(ubyte)[] buffer)
{
this.file.rawWrite(buffer);
return buffer.length; return buffer.length;
} }
void flush() { size_t write(const(char)[] str)
{
return write(cast(const(ubyte)[])str);
}
void flush()
{
this.file.flush(); this.file.flush();
} }
@property bool writeable() { return true; } @property bool writeable() { return true; }
} }
unittest
{
import dyaml.dumper, dyaml.loader, dyaml.node;
import std.file : readText, remove;
char[] test = ("Hello World : [Hello, World]\n" ~
"Answer: 42").dup;
//Read the input.
Node expected = Loader.fromString(test).load();
assert(expected["Hello World"][0] == "Hello");
assert(expected["Hello World"][1] == "World");
assert(expected["Answer"].as!int == 42);
//Dump the loaded document to output.yaml.
Dumper("output.yaml").dump(expected);
// Load the file and verify that it was saved correctly.
Node actual = Loader("output.yaml").load();
assert(actual["Hello World"][0] == "Hello");
assert(actual["Hello World"][1] == "World");
assert(actual["Answer"].as!int == 42);
assert(actual == expected);
// Clean up.
remove("output.yaml");
}

View file

@ -24,7 +24,7 @@ struct ZeroString(string TypeName)
@disable int opCmp(ref ZeroString); @disable int opCmp(ref ZeroString);
///Construct a string. ///Construct a string.
this(const string str) pure nothrow @safe this(const string str) pure nothrow @trusted
{ {
if(str is null || str == "") if(str is null || str == "")
{ {