The OpenD Programming Language

1 /++
2 $(H1 Annotated value)
3 
4 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
5 Authors: Ilia Ki 
6 Macros:
7 +/
8 module mir.annotated;
9 
10 import mir.internal.meta: basicElementType;
11 import mir.serde: serdeRegister, serdeAnnotation, serdeIsDynamicAlgebraic;
12 
13 static immutable excMsg = "At least one annotation is required to create an annotated value.";
14 version (D_Exceptions)
15     static immutable exc = new Exception(excMsg);
16 
17 /++
18 A convenience definition of an annotated value.
19 
20 A structure that behaves like a recursive algebraic type should define `enum _serdeRecursiveAlgebraic;` member.
21 +/
22 @serdeRegister
23 @serdeAnnotation
24 struct Annotated(T) {
25     ///
26     @serdeAnnotation
27     string[] annotations;
28 
29     static if (!(is(T == union) || is(T == struct)))
30         private enum _alloc_ = false;
31     else
32     static if (__traits(hasMember, T, "_serdeRecursiveAlgebraic"))
33         private enum _alloc_ = true;
34     else
35     {
36         import mir.algebraic: isVariant;
37         static if (isVariant!T)
38             private enum _alloc_ = true;
39         else
40             private enum _alloc_ = false;
41     }
42 
43     static if (_alloc_)
44     {
45         ///
46         private T* _value;
47         ///
48         ref inout(T) value() inout @property
49             in(_value)
50         {
51             return *_value;
52         }
53 
54         ///
55         ref T value(T value) @property return scope
56         {
57             if (_value is null)
58             {
59                 _value = new T;
60                 import core.lifetime: move;
61                 *_value = move(value);
62             }
63             return *_value;
64         }
65 
66         ///
67         bool opEquals(scope const Annotated rhs) scope const
68         {
69             return annotations == rhs.annotations && value == rhs.value;
70         }
71 
72         size_t toHash() scope @trusted const pure nothrow @nogc
73         {
74             static if (__traits(compiles, hashOf(value)))
75                 return hashOf(value);
76             else
77             {
78                 debug pragma(msg, "Mir warning: can't compute hash. Expexted `size_t toHash() scope @safe const pure nothrow @nogc` method for " ~ T.stringof);
79                 return cast(size_t)_value;
80             }
81         }
82     }
83     else
84     {
85         ///
86         T value;
87     }
88 
89 
90     /++
91     Params:
92         annotations = non-empty array of annotations
93         args = arguments to construct value with
94     +/
95     this(Args...)(string[] annotations, Args args) @safe pure {
96         if (annotations.length == 0)
97         {
98             version (D_Exceptions)
99                 { import mir.exception : toMutable; throw exc.toMutable; }
100             else
101                 assert(0, excMsg);
102         }
103         import core.lifetime: forward;
104         this.annotations = annotations;
105         static if (_alloc_)
106             this._value = new T(forward!args);
107         else
108         static if (__traits(compiles, value = args))
109             this.value = args;
110         else
111         static if (is(T == class))
112             this.value = new T(forward!args);
113         else
114             this.value = T(forward!args);
115     }
116 
117     // private alias E = .basicElementType!T;
118 
119     import std.traits: isAssociativeArray, isAggregateType;
120     ///
121     int opCmp()(scope const typeof(this) rhs) scope const pure nothrow @nogc @system
122         // if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp")))
123     {
124         if (auto d = __cmp(annotations, rhs.annotations))
125             return d;
126 
127         static if (__traits(compiles, __cmp(value, rhs.value)))
128             return __cmp(value, rhs.value);
129         else
130         static if (__traits(hasMember, value, "opCmp") && !is(T[i] == U*, U))
131             return value.opCmp(rhs.value);
132         else
133             return value < rhs.value ? -1 : value > rhs.value ? +1 : 0;
134     }
135 }
136 
137 ///
138 version(mir_test)
139 unittest
140 {
141     auto annotations = ["annotation"];
142     static struct S {double x;}
143     auto as = Annotated!S(annotations, 5);
144     assert(as.annotations == annotations);
145     assert(as.value.x == 5);
146 
147     static struct C {double x;}
148     auto ac = Annotated!S(annotations, 5);
149     assert(ac.annotations == annotations);
150     assert(ac.value.x == 5);
151 }
152 
153 ///
154 version(mir_test)
155 unittest
156 {
157     import mir.algebraic;
158     auto annotations = ["annotation"];
159     static struct S {double x;}
160     auto as = Annotated!(Variant!S)(annotations, 5);
161     assert(as.annotations == annotations);
162     assert(as.value.x == 5);
163 
164     static struct C {double x;}
165     auto ac = Annotated!(Variant!S)(annotations, 5);
166     assert(ac.annotations == annotations);
167     assert(ac.value.x == 5);
168 }
169 
170 /++
171 A convenience definition of an annotated value.
172 
173 A structure that behaves like a recursive algebraic type should define `enum _serdeRecursiveAlgebraic;` member.
174 +/
175 @serdeRegister
176 @serdeAnnotation
177 struct AnnotatedOnce(T) {
178     ///
179     @serdeAnnotation
180     string annotation;
181 
182     static if (!(is(T == union) || is(T == struct)))
183         private enum _alloc_ = false;
184     else
185     static if (__traits(hasMember, T, "_serdeRecursiveAlgebraic"))
186         private enum _alloc_ = true;
187     else
188     {
189         import mir.algebraic: isVariant;
190         static if (isVariant!T)
191             private enum _alloc_ = true;
192         else
193             private enum _alloc_ = false;
194     }
195 
196     static if (_alloc_)
197     {
198         ///
199         private T* _value;
200         ///
201         ref inout(T) value() inout @property
202         {
203             return *_value;
204         }
205 
206         ///
207         ref T value(T value) @property
208         {
209             if (_value is null)
210             {
211                 _value = new T;
212                 import core.lifetime: move;
213                 *_value = move(value);
214             }
215             return *_value;
216         }
217 
218         ///
219         bool opEquals(scope const AnnotatedOnce rhs) scope const
220         {
221             return annotation == rhs.annotation && value == rhs.value;
222         }
223     }
224     else
225     {
226         ///
227         T value;
228     }
229 
230 
231     /++
232     Params:
233         annotation = non-empty array of annotation
234         args = arguments to construct value with
235     +/
236     this(Args...)(string annotation, Args args) @safe pure {
237         import core.lifetime: forward;
238         this.annotation = annotation;
239         static if (_alloc_)
240             this._value = new T(forward!args);
241         else
242         static if (__traits(compiles, value = args))
243             this.value = args;
244         else
245         static if (is(T == class))
246             this.value = new T(forward!args);
247         else
248             this.value = T(forward!args);
249     }
250 
251     // private alias E = .basicElementType!T;
252 
253     import std.traits: isAssociativeArray, isAggregateType;
254     // static if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp")))
255     ///
256     int opCmp()(scope const typeof(this) rhs) scope const @system pure nothrow @nogc
257     {
258         if (auto d = __cmp(annotation, rhs.annotation))
259             return d;
260 
261         static if (__traits(compiles, __cmp(value, rhs.value)))
262             return __cmp(value, rhs.value);
263         else
264         static if (__traits(hasMember, value, "opCmp") && !is(T[i] == U*, U))
265             return value.opCmp(rhs.value);
266         else
267             return value < rhs.value ? -1 : value > rhs.value ? +1 : 0;
268     }
269 }
270 
271 ///
272 version(mir_test)
273 unittest
274 {
275     auto annotation = "annotation";
276     static struct S {double x;}
277     auto as = AnnotatedOnce!S(annotation, 5);
278     assert(as.annotation == annotation);
279     assert(as.value.x == 5);
280 
281     static struct C {double x;}
282     auto ac = AnnotatedOnce!S(annotation, 5);
283     assert(ac.annotation == annotation);
284     assert(ac.value.x == 5);
285 }
286 
287 ///
288 version(mir_test)
289 unittest
290 {
291     import mir.algebraic;
292     auto annotation = "annotation";
293     static struct S {double x;}
294     auto as = AnnotatedOnce!(Variant!S)(annotation, 5);
295     assert(as.annotation == annotation);
296     assert(as.value.x == 5);
297 
298     static struct C {double x;}
299     auto ac = AnnotatedOnce!(Variant!S)(annotation, 5);
300     assert(ac.annotation == annotation);
301     assert(ac.value.x == 5);
302 }