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([ArraySize.Size(ArraySize.Kind.const_, 42)]));
267         buf.shouldEqual("int x[42]");
268     }
269 
270     { // combine static array with dynamic
271         char[] buf;
272         arr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
273             buf ~= s;
274         }, CvQ(), DeclId("x"), ArraySize([ArraySize.Size(),
275                 ArraySize.Size(ArraySize.Kind.const_, 42)]));
276         buf.shouldEqual("int x[][42]");
277     }
278 }
279 
280 @safe struct PtrFmt {
281     TypeIdLR typeId;
282 
283     this(TypeId typeId) pure nothrow {
284         this.typeId.left = Left(typeId);
285     }
286 
287     this(TypeIdLR typeId) pure nothrow {
288         this.typeId = typeId;
289     }
290 
291     void toString(WriterL, WriterR)(scope WriterL wl, scope WriterR wr, CvQ cv_qual,
292             CvPtrQ[] cv_ptr_quals, DeclId decl_id) const {
293         import std.range.primitives : put;
294 
295         if (cv_qual != CvQ.Kind.none) {
296             cv_qual.toString(wl);
297             put(wl, " ");
298         }
299 
300         put(wl, typeId.left);
301 
302         if (cv_ptr_quals.length > 0) {
303             put(wl, " ");
304         }
305 
306         foreach (q; cv_ptr_quals) {
307             q.ptrQual.toString(wl);
308             q.cvQual.toString(wl);
309         }
310 
311         if (decl_id.length > 0 && cv_ptr_quals.length > 0
312                 && cv_ptr_quals[$ - 1].cvQual != CvQ.Kind.none) {
313             put(wl, " ");
314         }
315         put(wl, decl_id);
316 
317         put(wr, typeId.right);
318     }
319 }
320 
321 version (unittest) {
322     @("A PtrFmt in its basic shapes")
323     @Values([PtrQ.Kind.ptr, PtrQ.Kind.ref_])
324     unittest {
325         auto ptr = PtrFmt(TypeId("int"));
326 
327         auto kind = getValue!(PtrQ.Kind);
328         string kstr;
329         final switch (kind) {
330         case PtrQ.Kind.ref_:
331             kstr = "&";
332             break;
333         case PtrQ.Kind.ptr:
334             kstr = "*";
335             break;
336         }
337 
338         { // simplest
339             char[] buf;
340             ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
341                 buf ~= s;
342             }, CvQ(), null, DeclId(null));
343             buf.shouldEqual("int");
344         }
345 
346         { // simples ptr
347             char[] buf;
348             ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
349                 buf ~= s;
350             }, CvQ(), [CvPtrQ(CvQ(), PtrQ(kind))], DeclId("x"));
351             buf.shouldEqual("int " ~ kstr ~ "x");
352         }
353 
354         { // simples head const ptr
355             char[] buf;
356             ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
357                 buf ~= s;
358             }, CvQ.const_, [CvPtrQ(CvQ.const_, PtrQ(kind))], DeclId("x"));
359             buf.shouldEqual("const int " ~ kstr ~ "const x");
360         }
361 
362         { // ptr with varying cv-qualifier
363             char[] buf;
364             ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
365                 buf ~= s;
366             }, CvQ.const_, [// dfmt off
367                      CvPtrQ(CvQ.const_, PtrQ(kind)),
368                      CvPtrQ(CvQ(), PtrQ(kind)),
369                      CvPtrQ(CvQ.const_, PtrQ(kind)),
370                      CvPtrQ(CvQ(), PtrQ(kind)),
371                      CvPtrQ(CvQ(), PtrQ(kind)),
372                      // dfmt on
373             ], DeclId("x"));
374             buf.shouldEqual("const int " ~ kstr ~ "const" ~ kstr ~ kstr ~ "const" ~ kstr ~ kstr
375                     ~ "x");
376         }
377     }
378 }
379 
380 @safe struct FuncPtrFmt {
381     TypeIdLR typeId;
382 
383     this(TypeIdLR typeId) pure nothrow {
384         this.typeId = typeId;
385     }
386 
387     void toString(WriterL, WriterR)(scope WriterL wl, scope WriterR wr, CvQ cv_qual,
388             CvPtrQ[] cv_ptr_quals, DeclId decl_id) const {
389         import std.range.primitives : put;
390 
391         put(wl, typeId.left);
392 
393         if (cv_qual != CvQ.Kind.none) {
394             cv_qual.toString(wl);
395         }
396 
397         foreach (cq; cv_ptr_quals) {
398             cq.ptrQual.toString(wl);
399             cq.cvQual.toString(wl);
400         }
401 
402         if (decl_id.length > 0 && cv_ptr_quals.length > 0
403                 && cv_ptr_quals[$ - 1].cvQual != CvQ.Kind.none) {
404             put(wl, " ");
405         }
406         put(wl, decl_id);
407 
408         put(wr, typeId.right);
409     }
410 }
411 
412 version (unittest) {
413     @("A FuncPtrFmt")
414     @Values([PtrQ.Kind.ptr, PtrQ.Kind.ref_])
415     unittest {
416         auto ptr = FuncPtrFmt(TypeIdLR(Left("void ("), Right(")(int)")));
417 
418         auto kind = getValue!(PtrQ.Kind);
419         string kstr;
420         final switch (kind) {
421         case PtrQ.Kind.ref_:
422             kstr = "&";
423             break;
424         case PtrQ.Kind.ptr:
425             kstr = "*";
426             break;
427         }
428 
429         { // simplest
430             char[] buf;
431             ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
432                 buf ~= s;
433             }, CvQ(), null, DeclId(null));
434             buf.shouldEqual("void ()(int)");
435         }
436 
437         { // simples ptr
438             char[] buf;
439             ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
440                 buf ~= s;
441             }, CvQ(), [CvPtrQ(CvQ(), PtrQ(kind))], DeclId("x"));
442             buf.shouldEqual("void (" ~ kstr ~ "x)(int)");
443         }
444 
445         { // simples head const ptr
446             char[] buf;
447             ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
448                 buf ~= s;
449             }, CvQ.const_, [CvPtrQ(CvQ.const_, PtrQ(kind))], DeclId("x"));
450             buf.shouldEqual("void (const" ~ kstr ~ "const x)(int)");
451         }
452 
453         { // ptr with varying cv-qualifier
454             char[] buf;
455             ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
456                 buf ~= s;
457             }, CvQ.const_, [// dfmt off
458                      CvPtrQ(CvQ.const_, PtrQ(kind)),
459                      CvPtrQ(CvQ(), PtrQ(kind)),
460                      CvPtrQ(CvQ.const_, PtrQ(kind)),
461                      CvPtrQ(CvQ(), PtrQ(kind)),
462                      CvPtrQ(CvQ(), PtrQ(kind)),
463                      // dfmt on
464             ], DeclId("x"));
465             buf.shouldEqual(
466                     "void (const" ~ kstr ~ "const" ~ kstr ~ kstr ~ "const" ~ kstr ~ kstr ~ "x)(int)");
467         }
468     }
469 }
470 
471 @safe struct FuncFmt {
472     TypeIdLR typeId;
473 
474     this(TypeIdLR typeId) pure nothrow {
475         this.typeId = typeId;
476     }
477 
478     void toString(WriterL, WriterR)(scope WriterL wl, scope WriterR wr, DeclId decl_id) const {
479         import std.range.primitives : put;
480 
481         put(wl, typeId.left);
482 
483         put(wl, " ");
484 
485         if (decl_id.length > 0) {
486             put(wl, decl_id);
487         }
488 
489         put(wr, typeId.right);
490     }
491 }
492 
493 @("A FuncFmt")
494 unittest {
495     auto ptr = FuncFmt(TypeIdLR(Left("void"), Right("(int)")));
496 
497     { // simplest
498         char[] buf;
499         ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
500             buf ~= s;
501         }, DeclId(null));
502         buf.shouldEqual("void (int)");
503     }
504 
505     { // simples ptr
506         char[] buf;
507         ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
508             buf ~= s;
509         }, DeclId("x"));
510         buf.shouldEqual("void x(int)");
511     }
512 }
513 
514 @safe struct FuncSignatureFmt {
515     TypeIdLR typeId;
516 
517     this(TypeIdLR typeId) pure nothrow {
518         this.typeId = typeId;
519     }
520 
521     void toString(WriterL, WriterR)(scope WriterL wl, scope WriterR wr) const {
522         import std.range.primitives : put;
523 
524         put(wl, typeId.left);
525         put(wl, " ");
526         put(wr, typeId.right);
527     }
528 }
529 
530 @("A FuncFmt")
531 unittest {
532     auto ptr = FuncSignatureFmt(TypeIdLR(Left("void"), Right("(int)")));
533 
534     { // simplest
535         char[] buf;
536         ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
537             buf ~= s;
538         });
539         buf.shouldEqual("void (int)");
540     }
541 
542     { // simples ptr
543         char[] buf;
544         ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
545             buf ~= s;
546         });
547         buf.shouldEqual("void (int)");
548     }
549 }
550 
551 @safe struct CtorFmt {
552     TypeId typeId;
553 
554     this(TypeId typeId) pure nothrow {
555         this.typeId = typeId;
556     }
557 
558     void toString(Writer)(scope Writer w, DeclId decl_id) const {
559         import std.range.primitives : put;
560 
561         put(w, decl_id);
562         put(w, typeId);
563     }
564 }
565 
566 @safe struct DtorFmt {
567     void toString(Writer)(scope Writer w, DeclId decl_id) const {
568         import std.range.primitives : put;
569 
570         put(w, "~");
571         put(w, decl_id);
572         put(w, "()");
573     }
574 }