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 }