diff --git a/examples/resolver/main.d b/examples/resolver/main.d index 9bde099..f3c08a1 100644 --- a/examples/resolver/main.d +++ b/examples/resolver/main.d @@ -12,12 +12,10 @@ int main(string[] args) try { - auto resolver = new Resolver; - resolver.addImplicitResolver("!color", regex("[0-9a-fA-F]{6}"), - "0123456789abcdefABCDEF"); auto loader = Loader.fromFile("input.yaml"); - loader.resolver = resolver; + loader.resolver.addImplicitResolver("!color", regex("[0-9a-fA-F]{6}"), + "0123456789abcdefABCDEF"); auto root = loader.load(); diff --git a/examples/yaml_bench/yaml_bench.d b/examples/yaml_bench/yaml_bench.d index b9eff2b..7fccb3c 100644 --- a/examples/yaml_bench/yaml_bench.d +++ b/examples/yaml_bench/yaml_bench.d @@ -101,7 +101,7 @@ void main(string[] args) //@safe { // Instead of constructing a resolver/constructor with each Loader, // construct them once to remove noise when profiling. - auto resolver = new Resolver(); + auto resolver = Resolver.withDefaultResolvers; auto constructTime = stopWatch.peek(); @@ -122,7 +122,7 @@ void main(string[] args) //@safe return; } - loader.resolver = resolver; + loader.resolver = resolver; nodes = loader.array; } void runDumpBenchmark() @safe diff --git a/source/dyaml/composer.d b/source/dyaml/composer.d index 8ee0d4b..e09e7dd 100644 --- a/source/dyaml/composer.d +++ b/source/dyaml/composer.d @@ -79,7 +79,6 @@ struct Composer pure @safe nothrow ~this() { parser_ = null; - resolver_ = null; anchors_.destroy(); anchors_ = null; } diff --git a/source/dyaml/dumper.d b/source/dyaml/dumper.d index ef78fae..b2db0e7 100644 --- a/source/dyaml/dumper.d +++ b/source/dyaml/dumper.d @@ -99,7 +99,7 @@ struct Dumper(Range) */ this(Range stream) @safe { - resolver_ = new Resolver(); + resolver_ = Resolver.withDefaultResolvers; stream_ = stream; } @@ -110,9 +110,9 @@ struct Dumper(Range) } ///Specify custom Resolver to use. - @property void resolver(Resolver resolver) @safe + auto ref resolver() @safe { - resolver_ = resolver; + return resolver_; } ///Write scalars in _canonical form? @@ -290,10 +290,10 @@ struct Dumper(Range) ///Use a custom resolver to support custom data types and/or implicit tags @safe unittest { + import std.regex : regex; auto node = Node([1, 2, 3, 4, 5]); - auto resolver = new Resolver(); auto dumper = dumper(new Appender!string()); - dumper.resolver = resolver; + dumper.resolver.addImplicitResolver("!tag", regex("A.*"), "A"); dumper.dump(node); } /// Set default scalar style diff --git a/source/dyaml/loader.d b/source/dyaml/loader.d index 078b010..a819d54 100644 --- a/source/dyaml/loader.d +++ b/source/dyaml/loader.d @@ -149,6 +149,7 @@ struct Loader /// Ditto private this(ubyte[] yamlData) @safe { + resolver_ = Resolver.withDefaultResolvers; try { reader_ = new Reader(yamlData); @@ -170,9 +171,9 @@ struct Loader } /// Specify custom Resolver to use. - void resolver(Resolver resolver) pure @safe nothrow @nogc + auto ref resolver() pure @safe nothrow @nogc { - resolver_ = resolver; + return resolver_; } /** Load single YAML document. @@ -221,7 +222,6 @@ struct Loader static Composer composer; if (!rangeInitialized) { - lazyInitConstructorResolver(); composer = Composer(parser_, resolver_); rangeInitialized = true; } @@ -290,13 +290,6 @@ struct Loader { return parser_; } - - // Construct default constructor/resolver if the user has not yet specified - // their own. - void lazyInitConstructorResolver() @safe - { - if(resolver_ is null) { resolver_ = new Resolver(); } - } } /// Load single YAML document from a file: @safe unittest @@ -394,7 +387,7 @@ struct Loader writeln("Failed to read file 'example.yaml'"); } } -/// Use a custom constructor/resolver to support custom data types and/or implicit tags: +/// Use a custom resolver to support custom data types and/or implicit tags: @safe unittest { import std.file : write; @@ -404,11 +397,11 @@ struct Loader "Hello world!\n"~ "...\n" ); - auto resolver = new Resolver(); - - // Add constructor functions / resolver expressions here... auto loader = Loader.fromFile("example.yaml"); - loader.resolver = resolver; + + // Add resolver expressions here... + // loader.resolver.addImplicitResolver(...); + auto rootNode = loader.load(); } diff --git a/source/dyaml/resolver.d b/source/dyaml/resolver.d index ae6d9bf..4ebbecb 100644 --- a/source/dyaml/resolver.d +++ b/source/dyaml/resolver.d @@ -24,12 +24,53 @@ import dyaml.node; import dyaml.exception; +static Tuple!(string, "tag", Regex!char, "regexp", string, "chars")[] regexes; + +static this() @safe { + regexes ~= tuple!("tag", "regexp", "chars")("tag:yaml.org,2002:bool", + regex(r"^(?:yes|Yes|YES|no|No|NO|true|True|TRUE" ~ + "|false|False|FALSE|on|On|ON|off|Off|OFF)$"), + "yYnNtTfFoO"); + regexes ~= tuple!("tag", "regexp", "chars")("tag:yaml.org,2002:float", + regex(r"^(?:[-+]?([0-9][0-9_]*)\\.[0-9_]*" ~ + "(?:[eE][-+][0-9]+)?|[-+]?(?:[0-9][0-9_]" ~ + "*)?\\.[0-9_]+(?:[eE][-+][0-9]+)?|[-+]?" ~ + "[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]" ~ + "*|[-+]?\\.(?:inf|Inf|INF)|\\." ~ + "(?:nan|NaN|NAN))$"), + "-+0123456789."); + regexes ~= tuple!("tag", "regexp", "chars")("tag:yaml.org,2002:int", + regex(r"^(?:[-+]?0b[0-1_]+" ~ + "|[-+]?0[0-7_]+" ~ + "|[-+]?(?:0|[1-9][0-9_]*)" ~ + "|[-+]?0x[0-9a-fA-F_]+" ~ + "|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$"), + "-+0123456789"); + regexes ~= tuple!("tag", "regexp", "chars")("tag:yaml.org,2002:merge", regex(r"^<<$"), "<"); + regexes ~= tuple!("tag", "regexp", "chars")("tag:yaml.org,2002:null", + regex(r"^$|^(?:~|null|Null|NULL)$"), "~nN\0"); + regexes ~= tuple!("tag", "regexp", "chars")("tag:yaml.org,2002:timestamp", + 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]?[Tt]|[ \t]+[0-9]" ~ + "[0-9]?:[0-9][0-9]:[0-9][0-9]" ~ + "(?:\\.[0-9]*)?(?:[ \t]*Z|[-+][0-9]" ~ + "[0-9]?(?::[0-9][0-9])?)?$"), + "0123456789"); + regexes ~= tuple!("tag", "regexp", "chars")("tag:yaml.org,2002:value", regex(r"^=$"), "="); + + + //The following resolver is only for documentation purposes. It cannot work + //because plain scalars cannot start with '!', '&', or '*'. + regexes ~= tuple!("tag", "regexp", "chars")("tag:yaml.org,2002:yaml", regex(r"^(?:!|&|\*)$"), "!&*"); +} + /** * Resolves YAML tags (data types). * * Can be used to implicitly resolve custom data types of scalar values. */ -final class Resolver +struct Resolver { private: // Default tag to use for scalars. @@ -47,24 +88,21 @@ final class Resolver */ Tuple!(string, Regex!char)[][dchar] yamlImplicitResolvers_; + package: + static auto withDefaultResolvers() @safe + { + Resolver resolver; + foreach(pair; regexes) + { + resolver.addImplicitResolver(pair.tag, pair.regexp, pair.chars); + } + return resolver; + } + public: @disable bool opEquals(ref Resolver); @disable int opCmp(ref Resolver); - /** - * Construct a Resolver. - * - * If you don't want to implicitly resolve default YAML tags/data types, - * you can use defaultImplicitResolvers to disable default resolvers. - * - * Params: defaultImplicitResolvers = Use default YAML implicit resolvers? - */ - this(Flag!"useDefaultImplicitResolvers" defaultImplicitResolvers = Yes.useDefaultImplicitResolvers) - @safe - { - if(defaultImplicitResolvers){addImplicitResolvers();} - } - /** * Add an implicit scalar resolver. * @@ -105,9 +143,7 @@ final class Resolver write("example.yaml", "A"); auto loader = Loader.fromFile("example.yaml"); - auto resolver = new Resolver(); - resolver.addImplicitResolver("!tag", regex("A.*"), "A"); - loader.resolver = resolver; + loader.resolver.addImplicitResolver("!tag", regex("A.*"), "A"); auto node = loader.load(); assert(node.tag == "!tag"); @@ -169,7 +205,7 @@ final class Resolver } @safe unittest { - auto resolver = new Resolver(); + auto resolver = Resolver.withDefaultResolvers; bool tagMatch(string tag, string[] values) @safe { @@ -215,46 +251,4 @@ final class Resolver ///Returns: Default mapping tag. @property string defaultMappingTag() const pure @safe nothrow {return defaultMappingTag_;} - - private: - // Add default implicit resolvers. - void addImplicitResolvers() @safe - { - addImplicitResolver("tag:yaml.org,2002:bool", - regex(r"^(?:yes|Yes|YES|no|No|NO|true|True|TRUE" ~ - "|false|False|FALSE|on|On|ON|off|Off|OFF)$"), - "yYnNtTfFoO"); - addImplicitResolver("tag:yaml.org,2002:float", - regex(r"^(?:[-+]?([0-9][0-9_]*)\\.[0-9_]*" ~ - "(?:[eE][-+][0-9]+)?|[-+]?(?:[0-9][0-9_]" ~ - "*)?\\.[0-9_]+(?:[eE][-+][0-9]+)?|[-+]?" ~ - "[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]" ~ - "*|[-+]?\\.(?:inf|Inf|INF)|\\." ~ - "(?:nan|NaN|NAN))$"), - "-+0123456789."); - addImplicitResolver("tag:yaml.org,2002:int", - regex(r"^(?:[-+]?0b[0-1_]+" ~ - "|[-+]?0[0-7_]+" ~ - "|[-+]?(?:0|[1-9][0-9_]*)" ~ - "|[-+]?0x[0-9a-fA-F_]+" ~ - "|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$"), - "-+0123456789"); - addImplicitResolver("tag:yaml.org,2002:merge", regex(r"^<<$"), "<"); - addImplicitResolver("tag:yaml.org,2002:null", - regex(r"^$|^(?:~|null|Null|NULL)$"), "~nN\0"); - addImplicitResolver("tag:yaml.org,2002:timestamp", - 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]?[Tt]|[ \t]+[0-9]" ~ - "[0-9]?:[0-9][0-9]:[0-9][0-9]" ~ - "(?:\\.[0-9]*)?(?:[ \t]*Z|[-+][0-9]" ~ - "[0-9]?(?::[0-9][0-9])?)?$"), - "0123456789"); - addImplicitResolver("tag:yaml.org,2002:value", regex(r"^=$"), "="); - - - //The following resolver is only for documentation purposes. It cannot work - //because plain scalars cannot start with '!', '&', or '*'. - addImplicitResolver("tag:yaml.org,2002:yaml", regex(r"^(?:!|&|\*)$"), "!&*"); - } } diff --git a/source/dyaml/test/constructor.d b/source/dyaml/test/constructor.d index c8596db..67a9a2b 100644 --- a/source/dyaml/test/constructor.d +++ b/source/dyaml/test/constructor.d @@ -935,7 +935,6 @@ void testConstructor(string dataFilename, string codeDummy) @safe new Exception("Unimplemented constructor test: " ~ base)); auto loader = Loader.fromFile(dataFilename); - loader.resolver = new Resolver; Node[] exp = expected[base]; diff --git a/source/dyaml/test/emitter.d b/source/dyaml/test/emitter.d index 30082a4..b811318 100644 --- a/source/dyaml/test/emitter.d +++ b/source/dyaml/test/emitter.d @@ -90,7 +90,6 @@ void testEmitterOnData(string dataFilename, string canonicalFilename) @safe auto loader2 = Loader.fromString(emitStream.data); loader2.name = "TEST"; - loader2.resolver = new Resolver; auto newEvents = loader2.parse(); assert(compareEvents(events, newEvents)); } @@ -118,7 +117,6 @@ void testEmitterOnCanonical(string canonicalFilename) @safe } auto loader2 = Loader.fromString(emitStream.data); loader2.name = "TEST"; - loader2.resolver = new Resolver; auto newEvents = loader2.parse(); assert(compareEvents(events, newEvents)); } @@ -175,7 +173,6 @@ void testEmitterStyles(string dataFilename, string canonicalFilename) @safe } auto loader2 = Loader.fromString(emitStream.data); loader2.name = "TEST"; - loader2.resolver = new Resolver; auto newEvents = loader2.parse(); assert(compareEvents(events, newEvents)); }