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;
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     unittest {
327         foreach (kind; [PtrQ.Kind.ptr, PtrQ.Kind.ref_]) {
328             auto ptr = PtrFmt(TypeId("int"));
329 
330             string kstr;
331             final switch (kind) {
332             case PtrQ.Kind.ref_:
333                 kstr = "&";
334                 break;
335             case PtrQ.Kind.ptr:
336                 kstr = "*";
337                 break;
338             }
339 
340             { // simplest
341                 char[] buf;
342                 ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
343                     buf ~= s;
344                 }, CvQ(), null, DeclId(null));
345                 buf.shouldEqual("int");
346             }
347 
348             { // simples ptr
349                 char[] buf;
350                 ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
351                     buf ~= s;
352                 }, CvQ(), [CvPtrQ(CvQ(), PtrQ(kind))], DeclId("x"));
353                 buf.shouldEqual("int " ~ kstr ~ "x");
354             }
355 
356             { // simples head const ptr
357                 char[] buf;
358                 ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
359                     buf ~= s;
360                 }, CvQ.const_, [CvPtrQ(CvQ.const_, PtrQ(kind))], DeclId("x"));
361                 buf.shouldEqual("const int " ~ kstr ~ "const x");
362             }
363 
364             { // ptr with varying cv-qualifier
365                 char[] buf;
366                 ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
367                     buf ~= s;
368                 }, CvQ.const_, [// dfmt off
369                      CvPtrQ(CvQ.const_, PtrQ(kind)),
370                      CvPtrQ(CvQ(), PtrQ(kind)),
371                      CvPtrQ(CvQ.const_, PtrQ(kind)),
372                      CvPtrQ(CvQ(), PtrQ(kind)),
373                      CvPtrQ(CvQ(), PtrQ(kind)),
374                      // dfmt on
375                         ], DeclId("x"));
376                 buf.shouldEqual("const int " ~ kstr ~ "const" ~ kstr ~ kstr
377                         ~ "const" ~ kstr ~ kstr ~ "x");
378             }
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     unittest {
418         foreach (kind; [PtrQ.Kind.ptr, PtrQ.Kind.ref_]) {
419             auto ptr = FuncPtrFmt(TypeIdLR(Left("void ("), Right(")(int)")));
420 
421             string kstr;
422             final switch (kind) {
423             case PtrQ.Kind.ref_:
424                 kstr = "&";
425                 break;
426             case PtrQ.Kind.ptr:
427                 kstr = "*";
428                 break;
429             }
430 
431             { // simplest
432                 char[] buf;
433                 ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
434                     buf ~= s;
435                 }, CvQ(), null, DeclId(null));
436                 buf.shouldEqual("void ()(int)");
437             }
438 
439             { // simples ptr
440                 char[] buf;
441                 ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
442                     buf ~= s;
443                 }, CvQ(), [CvPtrQ(CvQ(), PtrQ(kind))], DeclId("x"));
444                 buf.shouldEqual("void (" ~ kstr ~ "x)(int)");
445             }
446 
447             { // simples head const ptr
448                 char[] buf;
449                 ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
450                     buf ~= s;
451                 }, CvQ.const_, [CvPtrQ(CvQ.const_, PtrQ(kind))], DeclId("x"));
452                 buf.shouldEqual("void (const" ~ kstr ~ "const x)(int)");
453             }
454 
455             { // ptr with varying cv-qualifier
456                 char[] buf;
457                 ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
458                     buf ~= s;
459                 }, CvQ.const_, [// dfmt off
460                      CvPtrQ(CvQ.const_, PtrQ(kind)),
461                      CvPtrQ(CvQ(), PtrQ(kind)),
462                      CvPtrQ(CvQ.const_, PtrQ(kind)),
463                      CvPtrQ(CvQ(), PtrQ(kind)),
464                      CvPtrQ(CvQ(), PtrQ(kind)),
465                      // dfmt on
466                         ], DeclId("x"));
467                 buf.shouldEqual(
468                         "void (const" ~ kstr ~ "const" ~ kstr ~ kstr ~ "const"
469                         ~ kstr ~ kstr ~ "x)(int)");
470             }
471         }
472     }
473 }
474 
475 @safe struct FuncFmt {
476     TypeIdLR typeId;
477 
478     this(TypeIdLR typeId) pure nothrow {
479         this.typeId = typeId;
480     }
481 
482     void toString(WriterL, WriterR)(scope WriterL wl, scope WriterR wr, DeclId decl_id) const {
483         import std.range.primitives : put;
484 
485         put(wl, typeId.left);
486 
487         put(wl, " ");
488 
489         if (decl_id.length > 0) {
490             put(wl, decl_id);
491         }
492 
493         put(wr, typeId.right);
494     }
495 }
496 
497 @("A FuncFmt")
498 unittest {
499     auto ptr = FuncFmt(TypeIdLR(Left("void"), Right("(int)")));
500 
501     { // simplest
502         char[] buf;
503         ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
504             buf ~= s;
505         }, DeclId(null));
506         buf.shouldEqual("void (int)");
507     }
508 
509     { // simples ptr
510         char[] buf;
511         ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
512             buf ~= s;
513         }, DeclId("x"));
514         buf.shouldEqual("void x(int)");
515     }
516 }
517 
518 @safe struct FuncSignatureFmt {
519     TypeIdLR typeId;
520 
521     this(TypeIdLR typeId) pure nothrow {
522         this.typeId = typeId;
523     }
524 
525     void toString(WriterL, WriterR)(scope WriterL wl, scope WriterR wr) const {
526         import std.range.primitives : put;
527 
528         put(wl, typeId.left);
529         put(wl, " ");
530         put(wr, typeId.right);
531     }
532 }
533 
534 @("A FuncFmt")
535 unittest {
536     auto ptr = FuncSignatureFmt(TypeIdLR(Left("void"), Right("(int)")));
537 
538     { // simplest
539         char[] buf;
540         ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
541             buf ~= s;
542         });
543         buf.shouldEqual("void (int)");
544     }
545 
546     { // simples ptr
547         char[] buf;
548         ptr.toString((const(char)[] s) { buf ~= s; }, (const(char)[] s) {
549             buf ~= s;
550         });
551         buf.shouldEqual("void (int)");
552     }
553 }
554 
555 @safe struct CtorFmt {
556     TypeId typeId;
557 
558     this(TypeId typeId) pure nothrow {
559         this.typeId = typeId;
560     }
561 
562     void toString(Writer)(scope Writer w, DeclId decl_id) const {
563         import std.range.primitives : put;
564 
565         put(w, decl_id);
566         put(w, typeId);
567     }
568 }
569 
570 @safe struct DtorFmt {
571     void toString(Writer)(scope Writer w, DeclId decl_id) const {
572         import std.range.primitives : put;
573 
574         put(w, "~");
575         put(w, decl_id);
576         put(w, "()");
577     }
578 }