1 /**
2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 
10 Type safe formatters for C/C++ types.
11 
12 Every formatter that have a TypeIdLR have a toString method that take a left
13 and right writer. Those are used to compose formatters.
14 Shift to the right writer after declaration-id is written.
15 
16 Example of types this module must be able to handle:
17 int x
18 void x(int)
19 int *x
20 void (*x)(int)
21 void (*const x)(int)
22 int const* x
23 int *const x
24 int x[3]
25 */
26 module cpptooling.data.kind_type_format;
27 
28 version (unittest) {
29     import unit_threaded : shouldEqual, Values, getValue;
30 }
31 
32 @safe struct Left {
33     string payload;
34     alias payload this;
35 }
36 
37 @safe struct Right {
38     string payload;
39     alias payload this;
40 }
41 
42 /// type-id
43 @safe struct TypeId {
44     string payload;
45     alias payload this;
46 }
47 
48 /// type-id where the declaration-id is in between two blocks
49 @safe struct TypeIdLR {
50     Left left;
51     Right right;
52 }
53 
54 /// declaration-id
55 @safe struct DeclId {
56     string payload;
57     alias payload this;
58 }
59 
60 /// cv-qualifier such as const/volatile
61 @safe struct CvQ {
62     enum Kind {
63         none,
64         const_,
65         volatile_
66     }
67 
68     Kind payload;
69     alias payload this;
70 
71     static auto const_() @safe {
72         return CvQ(Kind.const_);
73     }
74 
75     static auto volatile_() @safe {
76         return CvQ(Kind.volatile_);
77     }
78 
79     void toString(Writer)(scope Writer w) const {
80         import std.range.primitives : put;
81 
82         final switch (payload) {
83         case Kind.none:
84             break;
85         case Kind.const_:
86             put(w, "const");
87             break;
88         case Kind.volatile_:
89             put(w, "volatile");
90             break;
91         }
92     }
93 }
94 
95 /// ptr-qualifier such as */&.
96 @safe struct PtrQ {
97     /// Kind of pointer
98     enum Kind {
99         ptr,
100         ref_
101     }
102 
103     Kind payload;
104     alias payload this;
105 
106     static auto ptr() @safe {
107         return PtrQ(Kind.ptr);
108     }
109 
110     static auto ref_() @safe {
111         return PtrQ(Kind.ref_);
112     }
113 
114     void toString(Writer)(scope Writer w) const {
115         import std.range.primitives : put;
116 
117         final switch (payload) {
118         case Kind.ptr:
119             put(w, "*");
120             break;
121         case Kind.ref_:
122             put(w, "&");
123             break;
124         }
125     }
126 }
127 
128 /// Pair of ptr-qualifier and cv-qualifier.
129 @safe struct CvPtrQ {
130     CvQ cvQual;
131     PtrQ ptrQual;
132 }
133 
134 /// Size of an array.
135 @safe struct ArraySize {
136     enum Kind {
137         dynamic,
138         const_,
139     }
140 
141     static struct Size {
142         Kind kind;
143         long payload;
144         alias payload this;
145     }
146 
147     Size[] payload;
148     alias payload this;
149 }
150 
151 @safe struct SimpleFmt {
152     TypeId typeId;
153 
154     this(TypeId typeId) pure nothrow {
155         this.typeId = typeId;
156     }
157 
158     void toString(Writer)(scope Writer w, CvQ cv_qual, DeclId decl_id) const {
159         import std.range.primitives : put;
160 
161         if (cv_qual != CvQ.Kind.none) {
162             cv_qual.toString(w);
163             put(w, " ");
164         }
165 
166         put(w, typeId);
167 
168         if (decl_id.length > 0) {
169             put(w, " ");
170             put(w, decl_id);
171         }
172     }
173 }
174 
175 @("A simple type formatted with and without decl-id")
176 @safe unittest {
177     auto simple = SimpleFmt(TypeId("int"));
178 
179     {
180         char[] buf;
181         simple.toString((const(char)[] s) { buf ~= s; }, CvQ(), DeclId(null));
182         buf.shouldEqual("int");
183     }
184 
185     {
186         char[] buf;
187         simple.toString((const(char)[] s) { buf ~= s; }, CvQ(), DeclId("x"));
188         buf.shouldEqual("int x");
189     }
190 
191     {
192         char[] buf;
193         simple.toString((const(char)[] s) { buf ~= s; }, CvQ.const_, DeclId("y"));
194         buf.shouldEqual("const int y");
195     }
196 }
197 
198 @safe struct ArrayFmt {
199     TypeIdLR typeId;
200 
201     this(TypeId typeId) pure nothrow {
202         this.typeId.left = Left(typeId);
203     }
204 
205     this(TypeIdLR typeId) pure nothrow {
206         this.typeId = typeId;
207     }
208 
209     void toString(WriterL, WriterR)(scope WriterL wl, scope WriterR wr, CvQ cv_qual,
210             DeclId decl_id, ArraySize sz) const {
211         import std.conv : to;
212         import std.range.primitives : put;
213 
214         if (cv_qual != CvQ.Kind.none) {
215             cv_qual.toString(wl);
216             put(wl, " ");
217         }
218 
219         put(wl, typeId.left);
220 
221         if (decl_id.length > 0) {
222             put(wl, " ");
223             put(wl, decl_id);
224         }
225 
226         put(wr, typeId.right);
227 
228         foreach (ind; sz) {
229             put(wr, "[");
230             final switch (ind.kind) {
231             case ArraySize.Kind.const_:
232                 put(wr, ind.payload.to!string);
233                 break;
234             case ArraySize.Kind.dynamic:
235                 break;
236             }
237             put(wr, "]");
238         }
239     }
240 }
241 
242 @("An array type formatted")
243 @safe unittest {
244     auto arr = ArrayFmt(TypeId("int"));
245 
246     { // simplest case
247         char[] buf;
248         arr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
249             buf ~= s;
250         }, CvQ(), DeclId("x"), ArraySize([ArraySize.Size()]));
251         buf.shouldEqual("int x[]");
252     }
253 
254     { // const
255         char[] buf;
256         arr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
257             buf ~= s;
258         }, CvQ.const_, DeclId("x"), ArraySize([ArraySize.Size()]));
259         buf.shouldEqual("const int x[]");
260     }
261 
262     { // array with static value
263         char[] buf;
264         arr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
265             buf ~= s;
266         }, CvQ(), DeclId("x"), ArraySize([
267                     ArraySize.Size(ArraySize.Kind.const_, 42)
268                 ]));
269         buf.shouldEqual("int x[42]");
270     }
271 
272     { // combine static array with dynamic
273         char[] buf;
274         arr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
275             buf ~= s;
276         }, CvQ(), DeclId("x"), ArraySize([
277                     ArraySize.Size(), ArraySize.Size(ArraySize.Kind.const_, 42)
278                 ]));
279         buf.shouldEqual("int x[][42]");
280     }
281 }
282 
283 @safe struct PtrFmt {
284     TypeIdLR typeId;
285 
286     this(TypeId typeId) pure nothrow {
287         this.typeId.left = Left(typeId);
288     }
289 
290     this(TypeIdLR typeId) pure nothrow {
291         this.typeId = typeId;
292     }
293 
294     void toString(WriterL, WriterR)(scope WriterL wl, scope WriterR wr, CvQ cv_qual,
295             CvPtrQ[] cv_ptr_quals, DeclId decl_id) const {
296         import std.range.primitives : put;
297 
298         if (cv_qual != CvQ.Kind.none) {
299             cv_qual.toString(wl);
300             put(wl, " ");
301         }
302 
303         put(wl, typeId.left);
304 
305         if (cv_ptr_quals.length > 0) {
306             put(wl, " ");
307         }
308 
309         foreach (q; cv_ptr_quals) {
310             q.ptrQual.toString(wl);
311             q.cvQual.toString(wl);
312         }
313 
314         if (decl_id.length > 0 && cv_ptr_quals.length > 0
315                 && cv_ptr_quals[$ - 1].cvQual != CvQ.Kind.none) {
316             put(wl, " ");
317         }
318         put(wl, decl_id);
319 
320         put(wr, typeId.right);
321     }
322 }
323 
324 version (unittest) {
325     @("A PtrFmt in its basic shapes")
326     @Values([PtrQ.Kind.ptr, PtrQ.Kind.ref_])
327     unittest {
328         auto ptr = PtrFmt(TypeId("int"));
329 
330         auto kind = getValue!(PtrQ.Kind);
331         string kstr;
332         final switch (kind) {
333         case PtrQ.Kind.ref_:
334             kstr = "&";
335             break;
336         case PtrQ.Kind.ptr:
337             kstr = "*";
338             break;
339         }
340 
341         { // simplest
342             char[] buf;
343             ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
344                 buf ~= s;
345             }, CvQ(), null, DeclId(null));
346             buf.shouldEqual("int");
347         }
348 
349         { // simples ptr
350             char[] buf;
351             ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
352                 buf ~= s;
353             }, CvQ(), [CvPtrQ(CvQ(), PtrQ(kind))], DeclId("x"));
354             buf.shouldEqual("int " ~ kstr ~ "x");
355         }
356 
357         { // simples head const ptr
358             char[] buf;
359             ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
360                 buf ~= s;
361             }, CvQ.const_, [CvPtrQ(CvQ.const_, PtrQ(kind))], DeclId("x"));
362             buf.shouldEqual("const int " ~ kstr ~ "const x");
363         }
364 
365         { // ptr with varying cv-qualifier
366             char[] buf;
367             ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
368                 buf ~= s;
369             }, CvQ.const_, [// dfmt off
370                      CvPtrQ(CvQ.const_, PtrQ(kind)),
371                      CvPtrQ(CvQ(), PtrQ(kind)),
372                      CvPtrQ(CvQ.const_, PtrQ(kind)),
373                      CvPtrQ(CvQ(), PtrQ(kind)),
374                      CvPtrQ(CvQ(), PtrQ(kind)),
375                      // dfmt on
376                     ], DeclId("x"));
377             buf.shouldEqual("const int " ~ kstr ~ "const" ~ kstr ~ kstr ~ "const" ~ kstr ~ kstr
378                     ~ "x");
379         }
380     }
381 }
382 
383 @safe struct FuncPtrFmt {
384     TypeIdLR typeId;
385 
386     this(TypeIdLR typeId) pure nothrow {
387         this.typeId = typeId;
388     }
389 
390     void toString(WriterL, WriterR)(scope WriterL wl, scope WriterR wr, CvQ cv_qual,
391             CvPtrQ[] cv_ptr_quals, DeclId decl_id) const {
392         import std.range.primitives : put;
393 
394         put(wl, typeId.left);
395 
396         if (cv_qual != CvQ.Kind.none) {
397             cv_qual.toString(wl);
398         }
399 
400         foreach (cq; cv_ptr_quals) {
401             cq.ptrQual.toString(wl);
402             cq.cvQual.toString(wl);
403         }
404 
405         if (decl_id.length > 0 && cv_ptr_quals.length > 0
406                 && cv_ptr_quals[$ - 1].cvQual != CvQ.Kind.none) {
407             put(wl, " ");
408         }
409         put(wl, decl_id);
410 
411         put(wr, typeId.right);
412     }
413 }
414 
415 version (unittest) {
416     @("A FuncPtrFmt")
417     @Values([PtrQ.Kind.ptr, PtrQ.Kind.ref_])
418     unittest {
419         auto ptr = FuncPtrFmt(TypeIdLR(Left("void ("), Right(")(int)")));
420 
421         auto kind = getValue!(PtrQ.Kind);
422         string kstr;
423         final switch (kind) {
424         case PtrQ.Kind.ref_:
425             kstr = "&";
426             break;
427         case PtrQ.Kind.ptr:
428             kstr = "*";
429             break;
430         }
431 
432         { // simplest
433             char[] buf;
434             ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
435                 buf ~= s;
436             }, CvQ(), null, DeclId(null));
437             buf.shouldEqual("void ()(int)");
438         }
439 
440         { // simples ptr
441             char[] buf;
442             ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
443                 buf ~= s;
444             }, CvQ(), [CvPtrQ(CvQ(), PtrQ(kind))], DeclId("x"));
445             buf.shouldEqual("void (" ~ kstr ~ "x)(int)");
446         }
447 
448         { // simples head const ptr
449             char[] buf;
450             ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
451                 buf ~= s;
452             }, CvQ.const_, [CvPtrQ(CvQ.const_, PtrQ(kind))], DeclId("x"));
453             buf.shouldEqual("void (const" ~ kstr ~ "const x)(int)");
454         }
455 
456         { // ptr with varying cv-qualifier
457             char[] buf;
458             ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
459                 buf ~= s;
460             }, CvQ.const_, [// dfmt off
461                      CvPtrQ(CvQ.const_, PtrQ(kind)),
462                      CvPtrQ(CvQ(), PtrQ(kind)),
463                      CvPtrQ(CvQ.const_, PtrQ(kind)),
464                      CvPtrQ(CvQ(), PtrQ(kind)),
465                      CvPtrQ(CvQ(), PtrQ(kind)),
466                      // dfmt on
467                     ], DeclId("x"));
468             buf.shouldEqual(
469                     "void (const" ~ kstr ~ "const" ~ kstr ~ kstr ~ "const" ~ kstr ~ kstr ~ "x)(int)");
470         }
471     }
472 }
473 
474 @safe struct FuncFmt {
475     TypeIdLR typeId;
476 
477     this(TypeIdLR typeId) pure nothrow {
478         this.typeId = typeId;
479     }
480 
481     void toString(WriterL, WriterR)(scope WriterL wl, scope WriterR wr, DeclId decl_id) const {
482         import std.range.primitives : put;
483 
484         put(wl, typeId.left);
485 
486         put(wl, " ");
487 
488         if (decl_id.length > 0) {
489             put(wl, decl_id);
490         }
491 
492         put(wr, typeId.right);
493     }
494 }
495 
496 @("A FuncFmt")
497 unittest {
498     auto ptr = FuncFmt(TypeIdLR(Left("void"), Right("(int)")));
499 
500     { // simplest
501         char[] buf;
502         ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
503             buf ~= s;
504         }, DeclId(null));
505         buf.shouldEqual("void (int)");
506     }
507 
508     { // simples ptr
509         char[] buf;
510         ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
511             buf ~= s;
512         }, DeclId("x"));
513         buf.shouldEqual("void x(int)");
514     }
515 }
516 
517 @safe struct FuncSignatureFmt {
518     TypeIdLR typeId;
519 
520     this(TypeIdLR typeId) pure nothrow {
521         this.typeId = typeId;
522     }
523 
524     void toString(WriterL, WriterR)(scope WriterL wl, scope WriterR wr) const {
525         import std.range.primitives : put;
526 
527         put(wl, typeId.left);
528         put(wl, " ");
529         put(wr, typeId.right);
530     }
531 }
532 
533 @("A FuncFmt")
534 unittest {
535     auto ptr = FuncSignatureFmt(TypeIdLR(Left("void"), Right("(int)")));
536 
537     { // simplest
538         char[] buf;
539         ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
540             buf ~= s;
541         });
542         buf.shouldEqual("void (int)");
543     }
544 
545     { // simples ptr
546         char[] buf;
547         ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
548             buf ~= s;
549         });
550         buf.shouldEqual("void (int)");
551     }
552 }
553 
554 @safe struct CtorFmt {
555     TypeId typeId;
556 
557     this(TypeId typeId) pure nothrow {
558         this.typeId = typeId;
559     }
560 
561     void toString(Writer)(scope Writer w, DeclId decl_id) const {
562         import std.range.primitives : put;
563 
564         put(w, decl_id);
565         put(w, typeId);
566     }
567 }
568 
569 @safe struct DtorFmt {
570     void toString(Writer)(scope Writer w, DeclId decl_id) const {
571         import std.range.primitives : put;
572 
573         put(w, "~");
574         put(w, decl_id);
575         put(w, "()");
576     }
577 }