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 }