Implement range interface for Loader, remove old opApply interface (#196)

* Implement range interface for Loader, remove old opApply interface

* remove dead Composer.getSingleNode

* add Returns: lines to range primitives

* a ddoc fixup
This commit is contained in:
Cameron Ross 2018-09-04 12:31:56 -02:30 committed by BBasile
parent 9c2ae02792
commit 1f5eb76996
6 changed files with 70 additions and 94 deletions

View file

@ -45,6 +45,7 @@ void extract(ref Node document) @safe
void main(string[] args) //@safe
{
import std.array : array;
bool get = false;
bool dump = false;
bool reload = false;
@ -113,7 +114,7 @@ void main(string[] args) //@safe
loader.resolver = resolver;
loader.constructor = constructor;
nodes = loader.loadAll();
nodes = loader.array;
}
void runDumpBenchmark() @safe
{

View file

@ -114,27 +114,6 @@ final class Composer
return composeDocument();
}
///Get single YAML document, throwing if there is more than one document.
Node getSingleNode() @safe
{
assert(parser_.front.id != EventID.streamEnd,
"Trying to get a node from Composer when there is no node to " ~
"get. use checkNode() to determine if there is a node.");
Node document = composeDocument();
//Ensure that the stream contains no more documents.
enforce(parser_.front.id == EventID.streamEnd,
new ComposerException("Expected single document in the stream, " ~
"but found another document.",
parser_.front.startMark));
skipExpected(EventID.streamEnd);
assert(parser_.empty, "Found event after stream end");
return document;
}
private:
void skipExpected(const EventID id) @safe

View file

@ -47,6 +47,10 @@ struct Loader
string name_ = "<unknown>";
// Are we done loading?
bool done_;
// Last node read from stream
Node currentNode;
// Has the range interface been initialized yet?
bool rangeInitialized;
public:
@disable this();
@ -191,25 +195,12 @@ struct Loader
* or on a YAML parsing error.
*/
Node load() @safe
in
{
assert(!done_, "Loader: Trying to load YAML twice");
}
do
{
try
{
lazyInitConstructorResolver();
scope(exit) { done_ = true; }
auto composer = new Composer(parser_, resolver_, constructor_);
enforce(composer.checkNode(), new YAMLException("No YAML document to load"));
return composer.getSingleNode();
}
catch(YAMLException e)
{
throw new YAMLException("Unable to load YAML from %s : %s"
.format(name_, e.msg), e.file, e.line);
}
enforce!YAMLException(!empty, "Zero documents in stream");
auto output = front;
popFront();
enforce!YAMLException(empty, "More than one document in stream");
return output;
}
/** Load all YAML documents.
@ -224,64 +215,64 @@ struct Loader
*
* Throws: YAMLException on a parsing error.
*/
Node[] loadAll() @safe
deprecated("Redundant, loader is now an InputRange") Node[] loadAll() @safe
{
Node[] nodes;
foreach(ref node; this)
{
nodes ~= node;
import std.array: array;
return this.array;
}
return nodes;
}
/** Foreach over YAML documents.
/** Implements the empty range primitive.
*
* Parses documents lazily, when they are needed.
* If there's no more documents left in the stream, this will be true.
*
* Foreach over a Loader can only be used once; this is enforced by contract.
*
* Throws: YAMLException on a parsing error.
* Returns: `true` if no more documents left, `false` otherwise.
*/
int opApply(int delegate(ref Node) @safe dg) @safe
bool empty() @safe
{
return opApplyImpl(dg);
// currentNode and done_ are both invalid until popFront is called once
if (!rangeInitialized)
{
popFront();
}
/// Ditto
int opApply(int delegate(ref Node) @system dg) @system
{
return opApplyImpl(dg);
return done_;
}
package:
int opApplyImpl(T)(T dg)
in
/** Implements the popFront range primitive.
*
* Reads the next document from the stream, if possible.
*/
void popFront() @safe
{
assert(!done_, "Loader: Trying to load YAML twice");
}
do
{
scope(exit) { done_ = true; }
try
// Composer initialization is done here in case the constructor is
// modified, which is a pretty common case.
static Composer composer;
if (!rangeInitialized)
{
lazyInitConstructorResolver();
auto composer = new Composer(parser_, resolver_, constructor_);
int result;
while(composer.checkNode())
composer = new Composer(parser_, resolver_, constructor_);
rangeInitialized = true;
}
assert(!done_, "Loader.popFront called on empty range");
if (composer.checkNode())
{
auto node = composer.getNode();
result = dg(node);
if(result) { break; }
currentNode = composer.getNode();
}
return result;
}
catch(YAMLException e)
else
{
throw new YAMLException("Unable to load YAML from %s : %s "
.format(name_, e.msg), e.file, e.line);
done_ = true;
}
}
/** Implements the front range primitive.
*
* Returns: the current document as a Node.
*/
Node front() @safe
{
// currentNode and done_ are both invalid until popFront is called once
if (!rangeInitialized)
{
popFront();
}
return currentNode;
}
// Scan and return all tokens. Used for debugging.
Token[] scan() @safe
{
@ -353,6 +344,7 @@ struct Loader
/// Load all YAML documents from a file:
@safe unittest
{
import std.array : array;
import std.file : write;
write("example.yaml",
"---\n"~
@ -362,7 +354,7 @@ struct Loader
"Hello world 2!\n"~
"...\n"
);
auto nodes = Loader.fromFile("example.yaml").loadAll();
auto nodes = Loader.fromFile("example.yaml").array;
assert(nodes.length == 2);
}
/// Iterate over YAML documents in a file, lazily loading them:

View file

@ -34,8 +34,9 @@ void testParser(string dataFilename, string canonicalFilename) @safe
/// canonicalFilename = Another file to load, in canonical YAML format.
void testLoader(string dataFilename, string canonicalFilename) @safe
{
auto data = Loader.fromFile(dataFilename).loadAll();
auto canonical = Loader.fromFile(canonicalFilename).loadAll();
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)

View file

@ -20,8 +20,9 @@ import dyaml.test.common;
/// Params: errorFilename = File name to read from.
void testLoaderError(string errorFilename) @safe
{
import std.array : array;
Node[] nodes;
try { nodes = Loader.fromFile(errorFilename).loadAll(); }
try { nodes = Loader.fromFile(errorFilename).array; }
catch(YAMLException e)
{
printException(e);
@ -35,9 +36,10 @@ void testLoaderError(string errorFilename) @safe
/// Params: errorFilename = File name to read from.
void testLoaderErrorString(string errorFilename) @safe
{
import std.array : array;
try
{
auto nodes = Loader.fromFile(errorFilename).loadAll();
auto nodes = Loader.fromFile(errorFilename).array;
}
catch(YAMLException e)
{
@ -52,7 +54,8 @@ void testLoaderErrorString(string errorFilename) @safe
/// Params: errorFilename = File name to read from.
void testLoaderErrorFilename(string errorFilename) @safe
{
try { auto nodes = Loader.fromFile(errorFilename).loadAll(); }
import std.array : array;
try { auto nodes = Loader.fromFile(errorFilename).array; }
catch(YAMLException e)
{
printException(e);

View file

@ -68,7 +68,7 @@ void testRepresenterTypes(string codeFilename) @safe
auto loader = Loader.fromString(emitStream.data.toUTF8);
loader.name = "TEST";
loader.constructor = constructor;
readNodes = loader.loadAll();
readNodes = loader.array;
assert(expectedNodes.length == readNodes.length);
foreach(n; 0 .. expectedNodes.length)