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 }