diff options
author | Ralph Amissah <ralph@amissah.com> | 2016-10-01 14:12:13 -0400 |
---|---|---|
committer | Ralph Amissah <ralph@amissah.com> | 2019-04-10 15:14:13 -0400 |
commit | ba1712e77b31704fd9ba16d14e15518e7a7dd104 (patch) | |
tree | 1a0d3233fb611b68dbf43e098a41a0d9378e9ace /src/sdlang/taggedalgebraic/taggedalgebraic.d | |
parent | update sdlang, start looking to using dub remote dependencies (diff) |
0.7.0 using dub remote dependencies (local src related to sdlang removed)
Diffstat (limited to 'src/sdlang/taggedalgebraic/taggedalgebraic.d')
-rw-r--r-- | src/sdlang/taggedalgebraic/taggedalgebraic.d | 1085 |
1 files changed, 0 insertions, 1085 deletions
diff --git a/src/sdlang/taggedalgebraic/taggedalgebraic.d b/src/sdlang/taggedalgebraic/taggedalgebraic.d deleted file mode 100644 index ffaac49..0000000 --- a/src/sdlang/taggedalgebraic/taggedalgebraic.d +++ /dev/null @@ -1,1085 +0,0 @@ -/** - * Algebraic data type implementation based on a tagged union. - * - * Copyright: Copyright 2015, Sönke Ludwig. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Sönke Ludwig -*/ -module taggedalgebraic; - -import std.typetuple; - -// TODO: -// - distinguish between @property and non@-property methods. -// - verify that static methods are handled properly - -/** Implements a generic algebraic type using an enum to identify the stored type. - - This struct takes a `union` or `struct` declaration as an input and builds - an algebraic data type from its fields, using an automatically generated - `Kind` enumeration to identify which field of the union is currently used. - Multiple fields with the same value are supported. - - All operators and methods are transparently forwarded to the contained - value. The caller has to make sure that the contained value supports the - requested operation. Failure to do so will result in an assertion failure. - - The return value of forwarded operations is determined as follows: - $(UL - $(LI If the type can be uniquely determined, it is used as the return - value) - $(LI If there are multiple possible return values and all of them match - the unique types defined in the `TaggedAlgebraic`, a - `TaggedAlgebraic` is returned.) - $(LI If there are multiple return values and none of them is a - `Variant`, an `Algebraic` of the set of possible return types is - returned.) - $(LI If any of the possible operations returns a `Variant`, this is used - as the return value.) - ) -*/ -struct TaggedAlgebraic(U) if (is(U == union) || is(U == struct)) -{ - import std.algorithm : among; - import std.string : format; - import std.traits : FieldTypeTuple, FieldNameTuple, Largest, hasElaborateCopyConstructor, hasElaborateDestructor; - - private alias Union = U; - private alias FieldTypes = FieldTypeTuple!U; - private alias fieldNames = FieldNameTuple!U; - - static assert(FieldTypes.length > 0, "The TaggedAlgebraic's union type must have at least one field."); - static assert(FieldTypes.length == fieldNames.length); - - - private { - void[Largest!FieldTypes.sizeof] m_data = void; - Kind m_kind; - } - - /// A type enum that identifies the type of value currently stored. - alias Kind = TypeEnum!U; - - /// Compatibility alias - deprecated("Use 'Kind' instead.") alias Type = Kind; - - /// The type ID of the currently stored value. - @property Kind kind() const { return m_kind; } - - // Compatibility alias - deprecated("Use 'kind' instead.") - alias typeID = kind; - - // constructors - //pragma(msg, generateConstructors!U()); - mixin(generateConstructors!U); - - this(TaggedAlgebraic other) - { - import std.algorithm : swap; - swap(this, other); - } - - void opAssign(TaggedAlgebraic other) - { - import std.algorithm : swap; - swap(this, other); - } - - // postblit constructor - static if (anySatisfy!(hasElaborateCopyConstructor, FieldTypes)) - { - this(this) - { - switch (m_kind) { - default: break; - foreach (i, tname; fieldNames) { - alias T = typeof(__traits(getMember, U, tname)); - static if (hasElaborateCopyConstructor!T) - { - case __traits(getMember, Kind, tname): - typeid(T).postblit(cast(void*)&trustedGet!tname()); - return; - } - } - } - } - } - - // destructor - static if (anySatisfy!(hasElaborateDestructor, FieldTypes)) - { - ~this() - { - final switch (m_kind) { - foreach (i, tname; fieldNames) { - alias T = typeof(__traits(getMember, U, tname)); - case __traits(getMember, Kind, tname): - static if (hasElaborateDestructor!T) { - .destroy(trustedGet!tname); - } - return; - } - } - } - } - - /// Enables conversion or extraction of the stored value. - T opCast(T)() - { - import std.conv : to; - - final switch (m_kind) { - foreach (i, FT; FieldTypes) { - case __traits(getMember, Kind, fieldNames[i]): - static if (is(typeof(to!T(trustedGet!(fieldNames[i]))))) { - return to!T(trustedGet!(fieldNames[i])); - } else { - assert(false, "Cannot cast a "~(cast(Kind)m_kind).to!string~" value ("~FT.stringof~") to "~T.stringof); - } - } - } - assert(false); // never reached - } - /// ditto - T opCast(T)() const - { - // this method needs to be duplicated because inout doesn't work with to!() - import std.conv : to; - - final switch (m_kind) { - foreach (i, FT; FieldTypes) { - case __traits(getMember, Kind, fieldNames[i]): - static if (is(typeof(to!T(trustedGet!(fieldNames[i]))))) { - return to!T(trustedGet!(fieldNames[i])); - } else { - assert(false, "Cannot cast a "~(cast(Kind)m_kind).to!string~" value ("~FT.stringof~") to "~T.stringof); - } - } - } - assert(false); // never reached - } - - /// Uses `cast(string)`/`to!string` to return a string representation of the enclosed value. - string toString() const { return cast(string)this; } - - // NOTE: "this TA" is used here as the functional equivalent of inout, - // just that it generates one template instantiation per modifier - // combination, so that we can actually decide what to do for each - // case. - - /// Enables the invocation of methods of the stored value. - auto opDispatch(string name, this TA, ARGS...)(auto ref ARGS args) if (hasOp!(TA, OpKind.method, name, ARGS)) { return implementOp!(OpKind.method, name)(this, args); } - /// Enables accessing properties/fields of the stored value. - @property auto opDispatch(string name, this TA, ARGS...)(auto ref ARGS args) if (hasOp!(TA, OpKind.field, name, ARGS) && !hasOp!(TA, OpKind.method, name, ARGS)) { return implementOp!(OpKind.field, name)(this, args); } - /// Enables equality comparison with the stored value. - auto opEquals(T, this TA)(auto ref T other) if (hasOp!(TA, OpKind.binary, "==", T)) { return implementOp!(OpKind.binary, "==")(this, other); } - /// Enables relational comparisons with the stored value. - auto opCmp(T, this TA)(auto ref T other) if (hasOp!(TA, OpKind.binary, "<", T)) { assert(false, "TODO!"); } - /// Enables the use of unary operators with the stored value. - auto opUnary(string op, this TA)() if (hasOp!(TA, OpKind.unary, op)) { return implementOp!(OpKind.unary, op)(this); } - /// Enables the use of binary operators with the stored value. - auto opBinary(string op, T, this TA)(auto ref T other) if (hasOp!(TA, OpKind.binary, op, T)) { return implementOp!(OpKind.binary, op)(this, other); } - /// Enables the use of binary operators with the stored value. - auto opBinaryRight(string op, T, this TA)(auto ref T other) if (hasOp!(TA, OpKind.binaryRight, op, T)) { return implementOp!(OpKind.binaryRight, op)(this, other); } - /// Enables operator assignments on the stored value. - auto opOpAssign(string op, T, this TA)(auto ref T other) if (hasOp!(TA, OpKind.binary, op~"=", T)) { return implementOp!(OpKind.binary, op~"=")(this, other); } - /// Enables indexing operations on the stored value. - auto opIndex(this TA, ARGS...)(auto ref ARGS args) if (hasOp!(TA, OpKind.index, null, ARGS)) { return implementOp!(OpKind.index, null)(this, args); } - /// Enables index assignments on the stored value. - auto opIndexAssign(this TA, ARGS...)(auto ref ARGS args) if (hasOp!(TA, OpKind.indexAssign, null, ARGS)) { return implementOp!(OpKind.indexAssign, null)(this, args); } - /// Enables call syntax operations on the stored value. - auto opCall(this TA, ARGS...)(auto ref ARGS args) if (hasOp!(TA, OpKind.call, null, ARGS)) { return implementOp!(OpKind.call, null)(this, args); } - - private @trusted @property ref inout(typeof(__traits(getMember, U, f))) trustedGet(string f)() inout { return trustedGet!(inout(typeof(__traits(getMember, U, f)))); } - private @trusted @property ref inout(T) trustedGet(T)() inout { return *cast(inout(T)*)m_data.ptr; } -} - -/// -unittest -{ - import taggedalgebraic; - - struct Foo { - string name; - void bar() {} - } - - union Base { - int i; - string str; - Foo foo; - } - - alias Tagged = TaggedAlgebraic!Base; - - // Instantiate - Tagged taggedInt = 5; - Tagged taggedString = "Hello"; - Tagged taggedFoo = Foo(); - Tagged taggedAny = taggedInt; - taggedAny = taggedString; - taggedAny = taggedFoo; - - // Check type: Tagged.Kind is an enum - assert(taggedInt.kind == Tagged.Kind.i); - assert(taggedString.kind == Tagged.Kind.str); - assert(taggedFoo.kind == Tagged.Kind.foo); - assert(taggedAny.kind == Tagged.Kind.foo); - - // In most cases, can simply use as-is - auto num = 4 + taggedInt; - auto msg = taggedString ~ " World!"; - taggedFoo.bar(); - if (taggedAny.kind == Tagged.Kind.foo) // Make sure to check type first! - taggedAny.bar(); - //taggedString.bar(); // AssertError: Not a Foo! - - // Convert back by casting - auto i = cast(int) taggedInt; - auto str = cast(string) taggedString; - auto foo = cast(Foo) taggedFoo; - if (taggedAny.kind == Tagged.Kind.foo) // Make sure to check type first! - auto foo2 = cast(Foo) taggedAny; - //cast(Foo) taggedString; // AssertError! - - // Kind is an enum, so final switch is supported: - final switch (taggedAny.kind) { - case Tagged.Kind.i: - // It's "int i" - break; - - case Tagged.Kind.str: - // It's "string str" - break; - - case Tagged.Kind.foo: - // It's "Foo foo" - break; - } -} - -/** Operators and methods of the contained type can be used transparently. -*/ -@safe unittest { - static struct S { - int v; - int test() { return v / 2; } - } - - static union Test { - typeof(null) null_; - int integer; - string text; - string[string] dictionary; - S custom; - } - - alias TA = TaggedAlgebraic!Test; - - TA ta; - assert(ta.kind == TA.Kind.null_); - - ta = 12; - assert(ta.kind == TA.Kind.integer); - assert(ta == 12); - assert(cast(int)ta == 12); - assert(cast(long)ta == 12); - assert(cast(short)ta == 12); - - ta += 12; - assert(ta == 24); - assert(ta - 10 == 14); - - ta = ["foo" : "bar"]; - assert(ta.kind == TA.Kind.dictionary); - assert(ta["foo"] == "bar"); - - ta["foo"] = "baz"; - assert(ta["foo"] == "baz"); - - ta = S(8); - assert(ta.test() == 4); -} - -unittest { // std.conv integration - import std.conv : to; - - static struct S { - int v; - int test() { return v / 2; } - } - - static union Test { - typeof(null) null_; - int number; - string text; - } - - alias TA = TaggedAlgebraic!Test; - - TA ta; - assert(ta.kind == TA.Kind.null_); - ta = "34"; - assert(ta == "34"); - assert(to!int(ta) == 34, to!string(to!int(ta))); - assert(to!string(ta) == "34", to!string(ta)); -} - -/** Multiple fields are allowed to have the same type, in which case the type - ID enum is used to disambiguate. -*/ -@safe unittest { - static union Test { - typeof(null) null_; - int count; - int difference; - } - - alias TA = TaggedAlgebraic!Test; - - TA ta; - ta = TA(12, TA.Kind.count); - assert(ta.kind == TA.Kind.count); - assert(ta == 12); - - ta = null; - assert(ta.kind == TA.Kind.null_); -} - -unittest { - // test proper type modifier support - static struct S { - void test() {} - void testI() immutable {} - void testC() const {} - void testS() shared {} - void testSC() shared const {} - } - static union U { - S s; - } - - auto u = TaggedAlgebraic!U(S.init); - const uc = u; - immutable ui = cast(immutable)u; - //const shared usc = cast(shared)u; - //shared us = cast(shared)u; - - static assert( is(typeof(u.test()))); - static assert(!is(typeof(u.testI()))); - static assert( is(typeof(u.testC()))); - static assert(!is(typeof(u.testS()))); - static assert(!is(typeof(u.testSC()))); - - static assert(!is(typeof(uc.test()))); - static assert(!is(typeof(uc.testI()))); - static assert( is(typeof(uc.testC()))); - static assert(!is(typeof(uc.testS()))); - static assert(!is(typeof(uc.testSC()))); - - static assert(!is(typeof(ui.test()))); - static assert( is(typeof(ui.testI()))); - static assert( is(typeof(ui.testC()))); - static assert(!is(typeof(ui.testS()))); - static assert( is(typeof(ui.testSC()))); - - /*static assert(!is(typeof(us.test()))); - static assert(!is(typeof(us.testI()))); - static assert(!is(typeof(us.testC()))); - static assert( is(typeof(us.testS()))); - static assert( is(typeof(us.testSC()))); - - static assert(!is(typeof(usc.test()))); - static assert(!is(typeof(usc.testI()))); - static assert(!is(typeof(usc.testC()))); - static assert(!is(typeof(usc.testS()))); - static assert( is(typeof(usc.testSC())));*/ -} - -unittest { - // test attributes on contained values - import std.typecons : Rebindable, rebindable; - - class C { - void test() {} - void testC() const {} - void testI() immutable {} - } - union U { - Rebindable!(immutable(C)) c; - } - - auto ta = TaggedAlgebraic!U(rebindable(new immutable C)); - static assert(!is(typeof(ta.test()))); - static assert( is(typeof(ta.testC()))); - static assert( is(typeof(ta.testI()))); -} - -version (unittest) { - // test recursive definition using a wrapper dummy struct - // (needed to avoid "no size yet for forward reference" errors) - template ID(What) { alias ID = What; } - private struct _test_Wrapper { - TaggedAlgebraic!_test_U u; - alias u this; - this(ARGS...)(ARGS args) { u = TaggedAlgebraic!_test_U(args); } - } - private union _test_U { - _test_Wrapper[] children; - int value; - } - unittest { - alias TA = _test_Wrapper; - auto ta = TA(null); - ta ~= TA(0); - ta ~= TA(1); - ta ~= TA([TA(2)]); - assert(ta[0] == 0); - assert(ta[1] == 1); - assert(ta[2][0] == 2); - } -} - -unittest { // postblit/destructor test - static struct S { - static int i = 0; - bool initialized = false; - this(bool) { initialized = true; i++; } - this(this) { if (initialized) i++; } - ~this() { if (initialized) i--; } - } - - static struct U { - S s; - int t; - } - alias TA = TaggedAlgebraic!U; - { - assert(S.i == 0); - auto ta = TA(S(true)); - assert(S.i == 1); - { - auto tb = ta; - assert(S.i == 2); - ta = tb; - assert(S.i == 2); - ta = 1; - assert(S.i == 1); - ta = S(true); - assert(S.i == 2); - } - assert(S.i == 1); - } - assert(S.i == 0); - - static struct U2 { - S a; - S b; - } - alias TA2 = TaggedAlgebraic!U2; - { - auto ta2 = TA2(S(true), TA2.Kind.a); - assert(S.i == 1); - } - assert(S.i == 0); -} - -unittest { - static struct S { - union U { - int i; - string s; - U[] a; - } - alias TA = TaggedAlgebraic!U; - TA p; - alias p this; - } - S s = S(S.TA("hello")); - assert(cast(string)s == "hello"); -} - -unittest { // multiple operator choices - union U { - int i; - double d; - } - alias TA = TaggedAlgebraic!U; - TA ta = 12; - static assert(is(typeof(ta + 10) == TA)); // ambiguous, could be int or double - assert((ta + 10).kind == TA.Kind.i); - assert(ta + 10 == 22); - static assert(is(typeof(ta + 10.5) == double)); - assert(ta + 10.5 == 22.5); -} - -unittest { // Binary op between two TaggedAlgebraic values - union U { int i; } - alias TA = TaggedAlgebraic!U; - - TA a = 1, b = 2; - static assert(is(typeof(a + b) == int)); - assert(a + b == 3); -} - -unittest { // Ambiguous binary op between two TaggedAlgebraic values - union U { int i; double d; } - alias TA = TaggedAlgebraic!U; - - TA a = 1, b = 2; - static assert(is(typeof(a + b) == TA)); - assert((a + b).kind == TA.Kind.i); - assert(a + b == 3); -} - -unittest { - struct S { - union U { - @disableIndex string str; - S[] array; - S[string] object; - } - alias TA = TaggedAlgebraic!U; - TA payload; - alias payload this; - } - - S a = S(S.TA("hello")); - S b = S(S.TA(["foo": a])); - S c = S(S.TA([a])); - assert(b["foo"] == a); - assert(b["foo"] == "hello"); - assert(c[0] == a); - assert(c[0] == "hello"); -} - - -/** Tests if the algebraic type stores a value of a certain data type. -*/ -bool hasType(T, U)(in ref TaggedAlgebraic!U ta) -{ - alias Fields = Filter!(fieldMatchesType!(U, T), ta.fieldNames); - static assert(Fields.length > 0, "Type "~T.stringof~" cannot be stored in a "~(TaggedAlgebraic!U).stringof~"."); - - switch (ta.kind) { - default: return false; - foreach (i, fname; Fields) - case __traits(getMember, ta.Kind, fname): - return true; - } - assert(false); // never reached -} - -/// -unittest { - union Fields { - int number; - string text; - } - - TaggedAlgebraic!Fields ta = "test"; - - assert(ta.hasType!string); - assert(!ta.hasType!int); - - ta = 42; - assert(ta.hasType!int); - assert(!ta.hasType!string); -} - -unittest { // issue #1 - union U { - int a; - int b; - } - alias TA = TaggedAlgebraic!U; - - TA ta = TA(0, TA.Kind.b); - static assert(!is(typeof(ta.hasType!double))); - assert(ta.hasType!int); -} - -/** Gets the value stored in an algebraic type based on its data type. -*/ -ref inout(T) get(T, U)(ref inout(TaggedAlgebraic!U) ta) -{ - assert(hasType!(T, U)(ta)); - return ta.trustedGet!T; -} - -/// Convenience type that can be used for union fields that have no value (`void` is not allowed). -struct Void {} - -/// User-defined attibute to disable `opIndex` forwarding for a particular tagged union member. -@property auto disableIndex() { assert(__ctfe, "disableIndex must only be used as an attribute."); return DisableOpAttribute(OpKind.index, null); } - -private struct DisableOpAttribute { - OpKind kind; - string name; -} - - -private template hasOp(TA, OpKind kind, string name, ARGS...) -{ - import std.traits : CopyTypeQualifiers; - alias UQ = CopyTypeQualifiers!(TA, TA.Union); - enum hasOp = TypeTuple!(OpInfo!(UQ, kind, name, ARGS).fields).length > 0; -} - -unittest { - static struct S { - void m(int i) {} - bool opEquals(int i) { return true; } - bool opEquals(S s) { return true; } - } - - static union U { int i; string s; S st; } - alias TA = TaggedAlgebraic!U; - - static assert(hasOp!(TA, OpKind.binary, "+", int)); - static assert(hasOp!(TA, OpKind.binary, "~", string)); - static assert(hasOp!(TA, OpKind.binary, "==", int)); - static assert(hasOp!(TA, OpKind.binary, "==", string)); - static assert(hasOp!(TA, OpKind.binary, "==", int)); - static assert(hasOp!(TA, OpKind.binary, "==", S)); - static assert(hasOp!(TA, OpKind.method, "m", int)); - static assert(hasOp!(TA, OpKind.binary, "+=", int)); - static assert(!hasOp!(TA, OpKind.binary, "~", int)); - static assert(!hasOp!(TA, OpKind.binary, "~", int)); - static assert(!hasOp!(TA, OpKind.method, "m", string)); - static assert(!hasOp!(TA, OpKind.method, "m")); - static assert(!hasOp!(const(TA), OpKind.binary, "+=", int)); - static assert(!hasOp!(const(TA), OpKind.method, "m", int)); -} - -unittest { - struct S { - union U { - string s; - S[] arr; - S[string] obj; - } - alias TA = TaggedAlgebraic!(S.U); - TA payload; - alias payload this; - } - static assert(hasOp!(S.TA, OpKind.index, null, size_t)); - static assert(hasOp!(S.TA, OpKind.index, null, int)); - static assert(hasOp!(S.TA, OpKind.index, null, string)); - static assert(hasOp!(S.TA, OpKind.field, "length")); -} - -unittest { // "in" operator - union U { - string[string] dict; - } - alias TA = TaggedAlgebraic!U; - auto ta = TA(["foo": "bar"]); - assert("foo" in ta); - assert(*("foo" in ta) == "bar"); -} - -private static auto implementOp(OpKind kind, string name, T, ARGS...)(ref T self, auto ref ARGS args) -{ - import std.array : join; - import std.traits : CopyTypeQualifiers; - import std.variant : Algebraic, Variant; - alias UQ = CopyTypeQualifiers!(T, T.Union); - - alias info = OpInfo!(UQ, kind, name, ARGS); - - static assert(hasOp!(T, kind, name, ARGS)); - - static assert(info.fields.length > 0, "Implementing operator that has no valid implementation for any supported type."); - - //pragma(msg, "Fields for "~kind.stringof~" "~name~", "~T.stringof~": "~info.fields.stringof); - //pragma(msg, "Return types for "~kind.stringof~" "~name~", "~T.stringof~": "~info.ReturnTypes.stringof); - //pragma(msg, typeof(T.Union.tupleof)); - //import std.meta : staticMap; pragma(msg, staticMap!(isMatchingUniqueType!(T.Union), info.ReturnTypes)); - - switch (self.m_kind) { - default: assert(false, "Operator "~name~" ("~kind.stringof~") can only be used on values of the following types: "~[info.fields].join(", ")); - foreach (i, f; info.fields) { - alias FT = typeof(__traits(getMember, T.Union, f)); - case __traits(getMember, T.Kind, f): - static if (NoDuplicates!(info.ReturnTypes).length == 1) - return info.perform(self.trustedGet!FT, args); - else static if (allSatisfy!(isMatchingUniqueType!(T.Union), info.ReturnTypes)) - return TaggedAlgebraic!(T.Union)(info.perform(self.trustedGet!FT, args)); - else static if (allSatisfy!(isNoVariant, info.ReturnTypes)) { - alias Alg = Algebraic!(NoDuplicates!(info.ReturnTypes)); - info.ReturnTypes[i] ret = info.perform(self.trustedGet!FT, args); - import std.traits : isInstanceOf; - static if (isInstanceOf!(TaggedAlgebraic, typeof(ret))) return Alg(ret.payload); - else return Alg(ret); - } - else static if (is(FT == Variant)) - return info.perform(self.trustedGet!FT, args); - else - return Variant(info.perform(self.trustedGet!FT, args)); - } - } - - assert(false); // never reached -} - -unittest { // opIndex on recursive TA with closed return value set - static struct S { - union U { - char ch; - string str; - S[] arr; - } - alias TA = TaggedAlgebraic!U; - TA payload; - alias payload this; - - this(T)(T t) { this.payload = t; } - } - S a = S("foo"); - S s = S([a]); - - assert(implementOp!(OpKind.field, "length")(s.payload) == 1); - static assert(is(typeof(implementOp!(OpKind.index, null)(s.payload, 0)) == S.TA)); - assert(implementOp!(OpKind.index, null)(s.payload, 0) == "foo"); -} - -unittest { // opIndex on recursive TA with closed return value set using @disableIndex - static struct S { - union U { - @disableIndex string str; - S[] arr; - } - alias TA = TaggedAlgebraic!U; - TA payload; - alias payload this; - - this(T)(T t) { this.payload = t; } - } - S a = S("foo"); - S s = S([a]); - - assert(implementOp!(OpKind.field, "length")(s.payload) == 1); - static assert(is(typeof(implementOp!(OpKind.index, null)(s.payload, 0)) == S)); - assert(implementOp!(OpKind.index, null)(s.payload, 0) == "foo"); -} - - -private auto performOpRaw(U, OpKind kind, string name, T, ARGS...)(ref T value, /*auto ref*/ ARGS args) -{ - static if (kind == OpKind.binary) return mixin("value "~name~" args[0]"); - else static if (kind == OpKind.binaryRight) return mixin("args[0] "~name~" value"); - else static if (kind == OpKind.unary) return mixin("name "~value); - else static if (kind == OpKind.method) return __traits(getMember, value, name)(args); - else static if (kind == OpKind.field) return __traits(getMember, value, name); - else static if (kind == OpKind.index) return value[args]; - else static if (kind == OpKind.indexAssign) return value[args[1 .. $]] = args[0]; - else static if (kind == OpKind.call) return value(args); - else static assert(false, "Unsupported kind of operator: "~kind.stringof); -} - -unittest { - union U { int i; string s; } - - { int v = 1; assert(performOpRaw!(U, OpKind.binary, "+")(v, 3) == 4); } - { string v = "foo"; assert(performOpRaw!(U, OpKind.binary, "~")(v, "bar") == "foobar"); } -} - - -private auto performOp(U, OpKind kind, string name, T, ARGS...)(ref T value, /*auto ref*/ ARGS args) -{ - import std.traits : isInstanceOf; - static if (ARGS.length > 0 && isInstanceOf!(TaggedAlgebraic, ARGS[0])) { - static if (is(typeof(performOpRaw!(U, kind, name, T, ARGS)(value, args)))) { - return performOpRaw!(U, kind, name, T, ARGS)(value, args); - } else { - alias TA = ARGS[0]; - template MTypesImpl(size_t i) { - static if (i < TA.FieldTypes.length) { - alias FT = TA.FieldTypes[i]; - static if (is(typeof(&performOpRaw!(U, kind, name, T, FT, ARGS[1 .. $])))) - alias MTypesImpl = TypeTuple!(FT, MTypesImpl!(i+1)); - else alias MTypesImpl = TypeTuple!(MTypesImpl!(i+1)); - } else alias MTypesImpl = TypeTuple!(); - } - alias MTypes = NoDuplicates!(MTypesImpl!0); - static assert(MTypes.length > 0, "No type of the TaggedAlgebraic parameter matches any function declaration."); - static if (MTypes.length == 1) { - if (args[0].hasType!(MTypes[0])) - return performOpRaw!(U, kind, name)(value, args[0].get!(MTypes[0]), args[1 .. $]); - } else { - // TODO: allow all return types (fall back to Algebraic or Variant) - foreach (FT; MTypes) { - if (args[0].hasType!FT) - return ARGS[0](performOpRaw!(U, kind, name)(value, args[0].get!FT, args[1 .. $])); - } - } - throw new /*InvalidAgument*/Exception("Algebraic parameter type mismatch"); - } - } else return performOpRaw!(U, kind, name, T, ARGS)(value, args); -} - -unittest { - union U { int i; double d; string s; } - - { int v = 1; assert(performOp!(U, OpKind.binary, "+")(v, 3) == 4); } - { string v = "foo"; assert(performOp!(U, OpKind.binary, "~")(v, "bar") == "foobar"); } - { string v = "foo"; assert(performOp!(U, OpKind.binary, "~")(v, TaggedAlgebraic!U("bar")) == "foobar"); } - { int v = 1; assert(performOp!(U, OpKind.binary, "+")(v, TaggedAlgebraic!U(3)) == 4); } -} - - -private template OpInfo(U, OpKind kind, string name, ARGS...) -{ - import std.traits : CopyTypeQualifiers, FieldTypeTuple, FieldNameTuple, ReturnType; - - private alias FieldTypes = FieldTypeTuple!U; - private alias fieldNames = FieldNameTuple!U; - - private template isOpEnabled(string field) - { - alias attribs = TypeTuple!(__traits(getAttributes, __traits(getMember, U, field))); - template impl(size_t i) { - static if (i < attribs.length) { - static if (is(typeof(attribs[i]) == DisableOpAttribute)) { - static if (kind == attribs[i].kind && name == attribs[i].name) - enum impl = false; - else enum impl = impl!(i+1); - } else enum impl = impl!(i+1); - } else enum impl = true; - } - enum isOpEnabled = impl!0; - } - - template fieldsImpl(size_t i) - { - static if (i < FieldTypes.length) { - static if (isOpEnabled!(fieldNames[i]) && is(typeof(&performOp!(U, kind, name, FieldTypes[i], ARGS)))) { - alias fieldsImpl = TypeTuple!(fieldNames[i], fieldsImpl!(i+1)); - } else alias fieldsImpl = fieldsImpl!(i+1); - } else alias fieldsImpl = TypeTuple!(); - } - alias fields = fieldsImpl!0; - - template ReturnTypesImpl(size_t i) { - static if (i < fields.length) { - alias FT = CopyTypeQualifiers!(U, typeof(__traits(getMember, U, fields[i]))); - alias ReturnTypesImpl = TypeTuple!(ReturnType!(performOp!(U, kind, name, FT, ARGS)), ReturnTypesImpl!(i+1)); - } else alias ReturnTypesImpl = TypeTuple!(); - } - alias ReturnTypes = ReturnTypesImpl!0; - - static auto perform(T)(ref T value, auto ref ARGS args) { return performOp!(U, kind, name)(value, args); } -} - -private template ImplicitUnqual(T) { - import std.traits : Unqual, hasAliasing; - static if (is(T == void)) alias ImplicitUnqual = void; - else { - private static struct S { T t; } - static if (hasAliasing!S) alias ImplicitUnqual = T; - else alias ImplicitUnqual = Unqual!T; - } -} - -private enum OpKind { - binary, - binaryRight, - unary, - method, - field, - index, - indexAssign, - call -} - -private template TypeEnum(U) -{ - import std.array : join; - import std.traits : FieldNameTuple; - mixin("enum TypeEnum { " ~ [FieldNameTuple!U].join(", ") ~ " }"); -} - -private string generateConstructors(U)() -{ - import std.algorithm : map; - import std.array : join; - import std.string : format; - import std.traits : FieldTypeTuple; - - string ret; - - // disable default construction if first type is not a null/Void type - static if (!is(FieldTypeTuple!U[0] == typeof(null)) && !is(FieldTypeTuple!U[0] == Void)) - { - ret ~= q{ - @disable this(); - }; - } - - // normal type constructors - foreach (tname; UniqueTypeFields!U) - ret ~= q{ - this(typeof(U.%s) value) - { - m_data.rawEmplace(value); - m_kind = Kind.%s; - } - - void opAssign(typeof(U.%s) value) - { - if (m_kind != Kind.%s) { - // NOTE: destroy(this) doesn't work for some opDispatch-related reason - static if (is(typeof(&this.__xdtor))) - this.__xdtor(); - m_data.rawEmplace(value); - } else { - trustedGet!"%s" = value; - } - m_kind = Kind.%s; - } - }.format(tname, tname, tname, tname, tname, tname); - - // type constructors with explicit type tag - foreach (tname; AmbiguousTypeFields!U) - ret ~= q{ - this(typeof(U.%s) value, Kind type) - { - assert(type.among!(%s), format("Invalid type ID for type %%s: %%s", typeof(U.%s).stringof, type)); - m_data.rawEmplace(value); - m_kind = type; - } - }.format(tname, [SameTypeFields!(U, tname)].map!(f => "Kind."~f).join(", "), tname); - - return ret; -} - -private template UniqueTypeFields(U) { - import std.traits : FieldTypeTuple, FieldNameTuple; - - alias Types = FieldTypeTuple!U; - - template impl(size_t i) { - static if (i < Types.length) { - enum name = FieldNameTuple!U[i]; - alias T = Types[i]; - static if (staticIndexOf!(T, Types) == i && staticIndexOf!(T, Types[i+1 .. $]) < 0) - alias impl = TypeTuple!(name, impl!(i+1)); - else alias impl = TypeTuple!(impl!(i+1)); - } else alias impl = TypeTuple!(); - } - alias UniqueTypeFields = impl!0; -} - -private template AmbiguousTypeFields(U) { - import std.traits : FieldTypeTuple, FieldNameTuple; - - alias Types = FieldTypeTuple!U; - - template impl(size_t i) { - static if (i < Types.length) { - enum name = FieldNameTuple!U[i]; - alias T = Types[i]; - static if (staticIndexOf!(T, Types) == i && staticIndexOf!(T, Types[i+1 .. $]) >= 0) - alias impl = TypeTuple!(name, impl!(i+1)); - else alias impl = impl!(i+1); - } else alias impl = TypeTuple!(); - } - alias AmbiguousTypeFields = impl!0; -} - -unittest { - union U { - int a; - string b; - int c; - double d; - } - static assert([UniqueTypeFields!U] == ["b", "d"]); - static assert([AmbiguousTypeFields!U] == ["a"]); -} - -private template SameTypeFields(U, string field) { - import std.traits : FieldTypeTuple, FieldNameTuple; - - alias Types = FieldTypeTuple!U; - - alias T = typeof(__traits(getMember, U, field)); - template impl(size_t i) { - static if (i < Types.length) { - enum name = FieldNameTuple!U[i]; - static if (is(Types[i] == T)) - alias impl = TypeTuple!(name, impl!(i+1)); - else alias impl = TypeTuple!(impl!(i+1)); - } else alias impl = TypeTuple!(); - } - alias SameTypeFields = impl!0; -} - -private template MemberType(U) { - template MemberType(string name) { - alias MemberType = typeof(__traits(getMember, U, name)); - } -} - -private template isMatchingType(U) { - import std.traits : FieldTypeTuple; - enum isMatchingType(T) = staticIndexOf!(T, FieldTypeTuple!U) >= 0; -} - -private template isMatchingUniqueType(U) { - import std.traits : staticMap; - alias UniqueTypes = staticMap!(FieldTypeOf!U, UniqueTypeFields!U); - template isMatchingUniqueType(T) { - static if (is(T : TaggedAlgebraic!U)) enum isMatchingUniqueType = true; - else enum isMatchingUniqueType = staticIndexOfImplicit!(T, UniqueTypes) >= 0; - } -} - -private template fieldMatchesType(U, T) -{ - enum fieldMatchesType(string field) = is(typeof(__traits(getMember, U, field)) == T); -} - -private template FieldTypeOf(U) { - template FieldTypeOf(string name) { - alias FieldTypeOf = typeof(__traits(getMember, U, name)); - } -} - -private template staticIndexOfImplicit(T, Types...) { - template impl(size_t i) { - static if (i < Types.length) { - static if (is(T : Types[i])) enum impl = i; - else enum impl = impl!(i+1); - } else enum impl = -1; - } - enum staticIndexOfImplicit = impl!0; -} - -unittest { - static assert(staticIndexOfImplicit!(immutable(char), char) == 0); - static assert(staticIndexOfImplicit!(int, long) == 0); - static assert(staticIndexOfImplicit!(long, int) < 0); - static assert(staticIndexOfImplicit!(int, int, double) == 0); - static assert(staticIndexOfImplicit!(double, int, double) == 1); -} - - -private template isNoVariant(T) { - import std.variant : Variant; - enum isNoVariant = !is(T == Variant); -} - -private void rawEmplace(T)(void[] dst, ref T src) -{ - T* tdst = () @trusted { return cast(T*)dst.ptr; } (); - static if (is(T == class)) { - *tdst = src; - } else { - import std.conv : emplace; - emplace(tdst); - *tdst = src; - } -} |