diff --git a/source/dub/commandline.d b/source/dub/commandline.d index 6980f3f..61c53e6 100644 --- a/source/dub/commandline.d +++ b/source/dub/commandline.d @@ -35,7 +35,6 @@ import std.stdio; import std.string; import std.typecons : Tuple, tuple; -import std.variant; import std.path: setExtension; /** Retrieves a list of all available commands. @@ -598,8 +597,10 @@ */ class CommandArgs { struct Arg { - Variant defaultValue; - Variant value; + alias Value = SumType!(string[], string, bool, int, uint); + + Value defaultValue; + Value value; string names; string[] helpText; bool hidden; @@ -658,15 +659,24 @@ void getopt(T)(string names, T* var, void delegate(string, string) @safe parseValue, string[] help_text = null, bool hidden=false) { + import std.traits : OriginalType; + foreach (ref arg; m_recognizedArgs) if (names == arg.names) { assert(help_text is null, format!("Duplicated argument '%s' must not change helptext, consider to remove the duplication")(names)); - *var = arg.value.get!T; + *var = arg.value.match!( + (OriginalType!T v) => cast(T)v, + (_) { + if (false) + return T.init; + assert(false, "value from previous getopt has different type than the current getopt call"); + } + ); return; } assert(help_text.length > 0); Arg arg; - arg.defaultValue = *var; + arg.defaultValue = cast(OriginalType!T)*var; arg.names = names; arg.helpText = help_text; arg.hidden = hidden; @@ -674,7 +684,7 @@ m_args.getopt(config.passThrough, names, var); else m_args.getopt(config.passThrough, names, parseValue); - arg.value = *var; + arg.value = cast(OriginalType!T)*var; m_recognizedArgs ~= arg; } @@ -2725,13 +2735,16 @@ } else writeWS(longArgColumn); size_t col = longArgColumn; if (larg !is null) { - if (arg.defaultValue.peek!bool) { - writef("--%s", larg); - col += larg.length + 2; - } else { - writef("--%s=VALUE", larg); - col += larg.length + 8; - } + arg.defaultValue.match!( + (bool b) { + writef("--%s", larg); + col += larg.length + 2; + }, + (_) { + writef("--%s=VALUE", larg); + col += larg.length + 8; + } + ); } if (col < descColumn) { writeWS(descColumn - col); diff --git a/source/dub/internal/sdlang/ast.d b/source/dub/internal/sdlang/ast.d index deda488..d892ca2 100644 --- a/source/dub/internal/sdlang/ast.d +++ b/source/dub/internal/sdlang/ast.d @@ -23,6 +23,8 @@ import dub.internal.sdlang.token; import dub.internal.sdlang.util; +import dub.internal.dyaml.stdsumtype; + class Attribute { Value value; @@ -1102,7 +1104,10 @@ // Values foreach(val; values) - buf.put(" (%s): %s\n".format(.toString(val.type), val)); + buf.put(" (%s): %s\n".format( + val.match!(v => typeof(v).stringof), + val + )); // Attributes foreach(attrNamespace; _attributes.keys.sort()) @@ -1116,7 +1121,9 @@ buf.put( " %s%s(%s): %s\n".format( - namespaceStr, attr._name, .toString(attr.value.type), attr.value + namespaceStr, attr._name, + attr.value.match!(v => typeof(v).stringof), + attr.value ) ); } diff --git a/source/dub/internal/sdlang/parser.d b/source/dub/internal/sdlang/parser.d index 24bb16e..7fd1f38 100644 --- a/source/dub/internal/sdlang/parser.d +++ b/source/dub/internal/sdlang/parser.d @@ -7,7 +7,6 @@ else: import std.file; -import std.variant : Algebraic; import dub.internal.libInputVisitor; @@ -18,6 +17,8 @@ import dub.internal.sdlang.token; import dub.internal.sdlang.util; +import dub.internal.dyaml.stdsumtype; + /// Returns root tag. Tag parseFile(string filename) { @@ -119,7 +120,7 @@ } /// The element of the InputRange returned by pullParseFile and pullParseSource: -alias ParserEvent = Algebraic!( +alias ParserEvent = SumType!( FileStartEvent, FileEndEvent, TagStartEvent, @@ -483,41 +484,41 @@ auto eventRange = inputVisitor!ParserEvent( parser ); foreach(event; eventRange) { - if(auto e = event.peek!TagStartEvent()) - { - auto newTag = new Tag(currTag, e.namespace, e.name); - newTag.location = e.location; + event.match!( + (TagStartEvent e) + { + auto newTag = new Tag(currTag, e.namespace, e.name); + newTag.location = e.location; - currTag = newTag; - } - else if(event.peek!TagEndEvent()) - { - currTag = currTag.parent; + currTag = newTag; + }, + (TagEndEvent _) + { + currTag = currTag.parent; - if(!currTag) - parser.error("Internal Error: Received an extra TagEndEvent"); - } - else if(auto e = event.peek!ValueEvent()) - { - currTag.add(e.value); - } - else if(auto e = event.peek!AttributeEvent()) - { - auto attr = new Attribute(e.namespace, e.name, e.value, e.location); - currTag.add(attr); - } - else if(event.peek!FileStartEvent()) - { - // Do nothing - } - else if(event.peek!FileEndEvent()) - { - // There shouldn't be another parent. - if(currTag.parent) - parser.error("Internal Error: Unexpected end of file, not enough TagEndEvent"); - } - else - parser.error("Internal Error: Received unknown parser event"); + if(!currTag) + parser.error("Internal Error: Received an extra TagEndEvent"); + }, + (ValueEvent e) + { + currTag.add(e.value); + }, + (AttributeEvent e) + { + auto attr = new Attribute(e.namespace, e.name, e.value, e.location); + currTag.add(attr); + }, + (FileStartEvent _) + { + // Do nothing + }, + (FileEndEvent _) + { + // There shouldn't be another parent. + if(currTag.parent) + parser.error("Internal Error: Unexpected end of file, not enough TagEndEvent"); + } + ); } return currTag; diff --git a/source/dub/internal/sdlang/token.d b/source/dub/internal/sdlang/token.d index a86ae5d..c308ef0 100644 --- a/source/dub/internal/sdlang/token.d +++ b/source/dub/internal/sdlang/token.d @@ -13,7 +13,8 @@ import std.range; import std.string; import std.typetuple; -import std.variant; + +import dub.internal.dyaml.stdsumtype; import dub.internal.sdlang.symbol; import dub.internal.sdlang.util; @@ -80,16 +81,16 @@ Date Time (with an unknown timezone): DateTimeFracUnknownZone +/ alias TypeTuple!( + typeof(null), bool, string, dchar, int, long, float, double, real, Date, DateTimeFrac, SysTime, DateTimeFracUnknownZone, Duration, ubyte[], - typeof(null), ) ValueTypes; -alias Algebraic!( ValueTypes ) Value; ///ditto +alias SumType!ValueTypes Value; /// ditto template isSDLSink(T) { @@ -124,16 +125,7 @@ void toSDLString(Sink)(Value value, ref Sink sink) if(isOutputRange!(Sink,char)) { - foreach(T; ValueTypes) - { - if(value.type == typeid(T)) - { - toSDLString( value.get!T(), sink ); - return; - } - } - - throw new Exception("Internal SDLang-D error: Unhandled type of Value. Contains: "~value.toString()); + value.match!(v => toSDLString(v, sink)); } void toSDLString(Sink)(typeof(null) value, ref Sink sink) if(isOutputRange!(Sink,char)) @@ -362,7 +354,6 @@ { if( this.symbol != b.symbol || - this.value.type != b.value.type || this.value != b.value ) return false; diff --git a/source/dub/recipe/sdl.d b/source/dub/recipe/sdl.d index facc603..9e10e49 100644 --- a/source/dub/recipe/sdl.d +++ b/source/dub/recipe/sdl.d @@ -9,6 +9,7 @@ import dub.compilers.compiler; import dub.dependency; +import dub.internal.dyaml.stdsumtype; import dub.internal.logging; import dub.internal.sdlang; import dub.internal.vibecompat.inet.path; @@ -17,8 +18,7 @@ import std.algorithm : map; import std.array : array; import std.conv; -import std.string : startsWith; - +import std.string : startsWith, format; void parseSDL(ref PackageRecipe recipe, string sdl, string parent_name, string filename) { @@ -180,29 +180,29 @@ { enforceSDL(t.values.length != 0, "Missing dependency name.", t); enforceSDL(t.values.length == 1, "Multiple dependency names.", t); - auto pkg = expandPackageName(t.values[0].get!string, package_name, t); + auto pkg = expandPackageName(t.values[0].expect!string(t), package_name, t); enforceSDL(pkg !in bs.dependencies, "The dependency '"~pkg~"' is specified more than once.", t); Dependency dep = Dependency.any; auto attrs = t.attributes; if ("path" in attrs) { - dep = Dependency(NativePath(attrs["path"][0].value.get!string)); + dep = Dependency(NativePath(attrs["path"][0].value.expect!string(t, t.fullName ~ " path"))); } else if ("repository" in attrs) { enforceSDL("version" in attrs, "Missing version specification.", t); - dep = Dependency(Repository(attrs["repository"][0].value.get!string, - attrs["version"][0].value.get!string)); + dep = Dependency(Repository(attrs["repository"][0].value.expect!string(t, t.fullName ~ " repository"), + attrs["version"][0].value.expect!string(t, t.fullName ~ " version"))); } else { enforceSDL("version" in attrs, "Missing version specification.", t); - dep = Dependency(attrs["version"][0].value.get!string); + dep = Dependency(attrs["version"][0].value.expect!string(t, t.fullName ~ " version")); } if ("optional" in attrs) - dep.optional = attrs["optional"][0].value.get!bool; + dep.optional = attrs["optional"][0].value.expect!bool(t, t.fullName ~ " optional"); if ("default" in attrs) - dep.default_ = attrs["default"][0].value.get!bool; + dep.default_ = attrs["default"][0].value.expect!bool(t, t.fullName ~ " default"); bs.dependencies[pkg] = dep; @@ -317,7 +317,7 @@ private void parseToolchainRequirements(ref ToolchainRequirements tr, Tag tag) { foreach (attr; tag.attributes) - tr.addRequirement(attr.name, attr.value.get!string); + tr.addRequirement(attr.name, attr.value.expect!string(tag)); } private Tag toSDL(const ref ToolchainRequirements tr) @@ -334,7 +334,6 @@ private string expandPackageName(string name, string parent_name, Tag tag) { import std.algorithm : canFind; - import std.string : format; if (name.startsWith(":")) { enforceSDL(!parent_name.canFind(':'), format("Short-hand packages syntax not allowed within sub packages: %s -> %s", parent_name, name), tag); return parent_name ~ name; @@ -343,64 +342,79 @@ private string stringTagValue(Tag t, bool allow_child_tags = false) { - import std.string : format; enforceSDL(t.values.length > 0, format("Missing string value for '%s'.", t.fullName), t); enforceSDL(t.values.length == 1, format("Expected only one value for '%s'.", t.fullName), t); - enforceSDL(t.values[0].peek!string !is null, format("Expected value of type string for '%s'.", t.fullName), t); enforceSDL(allow_child_tags || t.tags.length == 0, format("No child tags allowed for '%s'.", t.fullName), t); // Q: should attributes be disallowed, or just ignored for forward compatibility reasons? //enforceSDL(t.attributes.length == 0, format("No attributes allowed for '%s'.", t.fullName), t); - return t.values[0].get!string; + return t.values[0].expect!string(t); +} + +private T expect(T)( + Value value, + Tag errorInfo, + string customFieldName = null, + string file = __FILE__, + int line = __LINE__ +) +{ + return value.match!( + (T v) => v, + (fallback) + { + enforceSDL(false, format("Expected value of type " ~ T.stringof ~ " for '%s', but got %s.", + customFieldName.length ? customFieldName : errorInfo.fullName, + typeof(fallback).stringof), + errorInfo, file, line); + return T.init; + } + ); } private string[] stringArrayTagValue(Tag t, bool allow_child_tags = false) { - import std.string : format; enforceSDL(allow_child_tags || t.tags.length == 0, format("No child tags allowed for '%s'.", t.fullName), t); // Q: should attributes be disallowed, or just ignored for forward compatibility reasons? //enforceSDL(t.attributes.length == 0, format("No attributes allowed for '%s'.", t.fullName), t); string[] ret; - foreach (v; t.values) { - enforceSDL(t.values[0].peek!string !is null, format("Values for '%s' must be strings.", t.fullName), t); - ret ~= v.get!string; + foreach (i, v; t.values) { + ret ~= v.expect!string(t, text(t.fullName, "[", i, "]")); } return ret; } -private void parsePlatformStringArray(Tag t, ref string[][string] dst) +private string getPlatformSuffix(Tag t, string file = __FILE__, int line = __LINE__) { string platform; if ("platform" in t.attributes) - platform = t.attributes["platform"][0].value.get!string; - dst[platform] ~= t.values.map!(v => v.get!string).array; + platform = t.attributes["platform"][0].value.expect!string(t, t.fullName ~ " platform", file, line); + return platform; +} + +private void parsePlatformStringArray(Tag t, ref string[][string] dst) +{ + string platform = t.getPlatformSuffix; + dst[platform] ~= t.values.map!(v => v.expect!string(t)).array; } private void parsePlatformStringAA(Tag t, ref string[string][string] dst) { - import std.string : format; - string platform; - if ("platform" in t.attributes) - platform = t.attributes["platform"][0].value.get!string; + string platform = t.getPlatformSuffix; enforceSDL(t.values.length == 2, format("Values for '%s' must be 2 required.", t.fullName), t); - enforceSDL(t.values[0].peek!string !is null, format("Values for '%s' must be strings.", t.fullName), t); - enforceSDL(t.values[1].peek!string !is null, format("Values for '%s' must be strings.", t.fullName), t); - dst[platform][t.values[0].get!string] = t.values[1].get!string; + dst[platform][t.values[0].expect!string(t)] = t.values[1].expect!string(t); } private void parsePlatformEnumArray(E, Es)(Tag t, ref Es[string] dst) { - string platform; - if ("platform" in t.attributes) - platform = t.attributes["platform"][0].value.get!string; + string platform = t.getPlatformSuffix; foreach (v; t.values) { if (platform !in dst) dst[platform] = Es.init; - dst[platform] |= v.get!string.to!E; + dst[platform] |= v.expect!string(t).to!E; } } private void enforceSDL(bool condition, lazy string message, Tag tag, string file = __FILE__, int line = __LINE__) { - import std.string : format; if (!condition) { throw new Exception(format("%s(%s): Error: %s", tag.location.file, tag.location.line + 1, message), file, line); }