1 /// Written in the D programming language. 2 /// Date: 2015, Joakim Brännström 3 /// License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 4 /// Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 module dsrcgen.c; 6 7 import std.typecons : Flag, Yes, No; 8 9 public import dsrcgen.base; 10 11 @safe: 12 13 ///TODO: change to c-comment and make a separate for c++. 14 /** Affected by attribute begin 15 * begin ~ comment 16 */ 17 class Comment : BaseModule { 18 mixin Attrs; 19 20 private string contents; 21 22 /// Create a one liner comment. 23 this(string contents) { 24 this.contents = contents; 25 } 26 27 /// 28 override string renderIndent(int parent_level, int level) { 29 if ("begin" in attrs) { 30 return indent(attrs["begin"] ~ contents, parent_level, level); 31 } 32 33 return indent("// " ~ contents, parent_level, level); 34 } 35 } 36 37 /// Mixin of methods for creating semantic C content. 38 mixin template CModuleX(T) { 39 mixin Attrs; 40 41 /** Access to self. 42 * 43 * Useful in with-statements. 44 */ 45 T _() { 46 return this; 47 } 48 49 Comment comment(string comment) { 50 auto e = new Comment(comment); 51 append(e); 52 e.sep; 53 return e; 54 } 55 56 Text!T text(string content) { 57 auto e = new Text!T(content); 58 append(e); 59 return e; 60 } 61 62 T base() { 63 auto e = new T; 64 append(e); 65 return e; 66 } 67 68 // Statements 69 Stmt!T stmt(string stmt_, Flag!"addSep" separator = Yes.addSep) { 70 auto e = new Stmt!T(stmt_); 71 append(e); 72 if (separator) { 73 sep(); 74 } 75 return e; 76 } 77 78 auto break_() { 79 return stmt("break"); 80 } 81 82 auto call(string name, string params) { 83 import std.format : format; 84 85 auto e = stmt(format("%s(%s)", name, params)); 86 return e; 87 } 88 89 auto call(T...)(string name, auto ref T args) { 90 import std.format : format; 91 92 string params = this.paramsToString(args); 93 94 auto e = stmt(format("%s(%s)", name, params)); 95 return e; 96 } 97 98 auto continue_() { 99 return stmt("continue"); 100 } 101 102 auto return_() { 103 return stmt("return"); 104 } 105 106 auto return_(string expr) { 107 return stmt("return " ~ expr); 108 } 109 110 auto goto_(string name) { 111 import std.format : format; 112 113 return stmt(format("goto %s", name)); 114 } 115 116 auto label(string name) { 117 import std.format : format; 118 119 return stmt(format("%s:", name)); 120 } 121 122 auto define(string name) { 123 import std.format : format; 124 125 auto e = stmt(format("#define %s", name)); 126 e[$.end = ""]; 127 return e; 128 } 129 130 auto define(string name, string value) { 131 import std.format : format; 132 133 // may need to replace \n with \\\n 134 auto e = stmt(format("#define %s %s", name, value)); 135 e[$.end = ""]; 136 return e; 137 } 138 139 auto extern_decl(string value) { 140 import std.format : format; 141 142 return stmt(format(`extern %s`, value)); 143 } 144 145 /// Wrap the supplied module in an extern statement. 146 auto extern_(string kind = null) { 147 import std.format : format; 148 149 T e; 150 151 if (kind.length == 0) { 152 e = stmt("extern ", No.addSep); 153 } else { 154 e = stmt(format(`extern "%s" `, kind), No.addSep); 155 } 156 157 e.suppressIndent(1); 158 e[$.end = ""]; 159 return e; 160 } 161 162 auto include(string filename) { 163 import std.format : format; 164 165 string f = filename; 166 string incl; 167 168 if (f.length > 1 && f[0] == '<') { 169 incl = format("#include %s", f); 170 } else { 171 incl = format(`#include "%s"`, f); 172 } 173 174 auto e = stmt(incl)[$.end = ""]; 175 return e; 176 } 177 178 // Suites 179 Suite!T suite(string headline, Flag!"addSep" separator = Yes.addSep) { 180 auto e = new Suite!T(headline); 181 append(e); 182 if (separator) { 183 sep(); 184 } 185 return e; 186 } 187 188 auto struct_(string name) { 189 auto e = suite("struct " ~ name)[$.end = "};"]; 190 return e; 191 } 192 193 auto if_(string cond) { 194 import std.format : format; 195 196 return suite(format("if (%s)", cond)); 197 } 198 199 auto else_if(string cond) { 200 import std.format : format; 201 202 return suite(format("else if (%s)", cond)); 203 } 204 205 auto else_() { 206 return suite("else"); 207 } 208 209 auto enum_() { 210 return suite("enum")[$.end = "};"]; 211 } 212 213 auto enum_(string identifier) { 214 return suite("enum " ~ identifier)[$.end = "};"]; 215 } 216 217 auto enum_const(string name) { 218 return stmt(name)[$.end = ","]; 219 } 220 221 auto for_(string init, string cond, string next) { 222 import std.format : format; 223 224 return suite(format("for (%s; %s; %s)", init, cond, next)); 225 } 226 227 auto while_(string cond) { 228 import std.format : format; 229 230 return suite(format("while (%s)", cond)); 231 } 232 233 auto do_while(string cond) { 234 import std.format : format; 235 236 auto e = suite("do"); 237 e[$.end = format("} while (%s);", cond)]; 238 return e; 239 } 240 241 auto switch_(string cond) { 242 import std.format : format; 243 244 return suite(format("switch (%s)", cond)); 245 } 246 247 auto case_(string val) { 248 import std.format : format; 249 250 auto e = suite(format("case %s:", val), No.addSep)[$.begin = "", $.end = ""]; 251 e.sep; 252 return e; 253 } 254 255 auto default_() { 256 auto e = suite("default:", No.addSep)[$.begin = "", $.end = ""]; 257 e.sep; 258 return e; 259 } 260 261 auto func(string return_type, string name) { 262 import std.format : format; 263 264 auto e = stmt(format("%s %s()", return_type, name)); 265 return e; 266 } 267 268 auto func(T...)(string return_type, string name, auto ref T args) { 269 import std.format : format; 270 271 string params = paramsToString(args); 272 273 auto e = stmt(format("%s %s(%s)", return_type, name, params)); 274 return e; 275 } 276 277 auto func_body(string return_type, string name) { 278 import std.format : format; 279 280 auto e = suite(format("%s %s()", return_type, name)); 281 return e; 282 } 283 284 auto func_body(T...)(string return_type, string name, auto ref T args) { 285 import std.format : format; 286 287 string params = paramsToString(args); 288 289 auto e = suite(format("%s %s(%s)", return_type, name, params)); 290 return e; 291 } 292 293 auto IF(string name) { 294 auto e = suite("#if " ~ name); 295 e[$.begin = "", $.end = "#endif // " ~ name]; 296 e.sep; 297 e.suppressIndent(1); 298 return e; 299 } 300 301 auto IFDEF(string name) { 302 import std.format : format; 303 304 auto e = suite(format("#ifdef %s", name)); 305 e[$.begin = "", $.end = "#endif // " ~ name]; 306 e.sep; 307 e.suppressIndent(1); 308 return e; 309 } 310 311 auto IFNDEF(string name) { 312 auto e = suite("#ifndef " ~ name); 313 e[$.begin = "", $.end = "#endif // " ~ name]; 314 e.sep; 315 e.suppressIndent(1); 316 return e; 317 } 318 319 auto ELIF(string cond) { 320 auto e = stmt("#elif " ~ cond); 321 return e; 322 } 323 324 auto ELSE() { 325 auto e = stmt("#else"); 326 return e; 327 } 328 } 329 330 string paramsToString(T...)(auto ref T args) { 331 import std.conv : to; 332 333 string params; 334 if (args.length >= 1) { 335 params = to!string(args[0]); 336 } 337 if (args.length >= 2) { 338 foreach (v; args[1 .. $]) { 339 params ~= ", " ~ to!string(v); 340 } 341 } 342 return params; 343 } 344 345 /// Represent a semantic item in C source. 346 class CModule : BaseModule { 347 mixin CModuleX!(CModule); 348 } 349 350 private string stmt_append_end(string s, ref const string[string] attrs) pure nothrow { 351 import std.algorithm : among; 352 353 //TODO too much null checking, refactor. 354 355 if (s.length == 0) { 356 string end = ";"; 357 if (auto v = "end" in attrs) { 358 end = *v; 359 } 360 s ~= end; 361 } else { 362 bool in_pattern = false; 363 try { 364 in_pattern = s[$ - 1].among(';', ':', ',', '{') != 0; 365 } 366 catch (Exception e) { 367 } 368 369 if (!in_pattern && s[0] != '#') { 370 string end = ";"; 371 if (auto v = "end" in attrs) { 372 end = *v; 373 } 374 s ~= end; 375 } 376 } 377 378 return s; 379 } 380 381 /** Affected by attribute end. 382 * stmt ~ end 383 * <recursive> 384 */ 385 class Stmt(T) : T { 386 private string headline; 387 388 /// Content of the statement. 389 this(string headline) { 390 this.headline = headline; 391 } 392 393 override string renderIndent(int parent_level, int level) { 394 string r = stmt_append_end(headline, attrs); 395 396 if ("noindent" !in attrs) { 397 r = indent(r, parent_level, level); 398 } 399 400 return r; 401 } 402 } 403 404 /** Affected by attribute begin, end, noindent. 405 * headline ~ begin 406 * <recursive> 407 * end 408 * noindent affects post_recursive. If set no indention there. 409 * r.length > 0 catches the case when begin or end is empty string. Used in switch/case. 410 */ 411 class Suite(T) : T { 412 private string headline; 413 414 /// Content of the suite/block. 415 this(string headline) { 416 this.headline = headline; 417 } 418 419 override string renderIndent(int parent_level, int level) { 420 import std.ascii : newline; 421 422 string r = headline ~ " {" ~ newline; 423 if (auto v = "begin" in attrs) { 424 r = headline ~ *v; 425 } 426 427 if (r.length > 0 && !("noindent" in attrs)) { 428 r = indent(r, parent_level, level); 429 } 430 return r; 431 } 432 433 override string renderPostRecursive(int parent_level, int level) { 434 string r = "}"; 435 if (auto v = "end" in attrs) { 436 r = *v; 437 } 438 439 if (r.length > 0 && "noindent" !in attrs) { 440 r = indent(r, parent_level, level); 441 } 442 return r; 443 } 444 } 445 446 /// An expressioin in C. 447 struct E { 448 @safe pure: 449 import std.conv : to; 450 451 private string content; 452 453 /// Content of the expression. 454 this(string content) nothrow pure { 455 this.content = content; 456 } 457 458 /// Convert argument via std.conv.to!string. 459 this(T)(T content) nothrow pure { 460 this.content = to!string(content); 461 } 462 463 /// Concatenate two expressions with ".". 464 this(E lhs, string rhs) nothrow pure { 465 this.content = lhs.content ~ "." ~ rhs; 466 } 467 468 /// ditto 469 auto e(string lhs) nothrow pure const { 470 return E(content ~ "." ~ lhs); 471 } 472 473 /// ditto 474 auto e(E lhs) nothrow pure const { 475 return E(content ~ "." ~ lhs.content); 476 } 477 478 /// Represent the semantic function call. 479 auto opCall(T...)(auto ref T value) pure const { 480 return E(content ~ "(" ~ paramsToString(value) ~ ")"); 481 } 482 483 // implicit 484 @property string toString() pure const nothrow { 485 return content; 486 } 487 488 alias toString this; 489 490 /// String representation of the content. Explicit cast. 491 T opCast(T : string)() pure const nothrow { 492 return content; 493 } 494 495 /// Preprend the textual representation of the operator to the content. 496 auto opUnary(string op)() pure nothrow const { 497 static if (op == "+" || op == "-" || op == "*" || op == "++" || op == "--") { 498 return E(mixin("\"" ~ op ~ "\"~content")); 499 } else { 500 static assert(0, "Operator " ~ op ~ " not implemented"); 501 } 502 } 503 504 /** Represent the semantic meaning of binary operators. 505 * 506 * ~ is special cased but OK for it doesn't exist in C/C++. 507 */ 508 auto opBinary(string op, T)(in T rhs) pure nothrow const { 509 static if (op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op == "&") { 510 return E(mixin("content~\" " ~ op ~ " \"~to!string(rhs)")); 511 } else static if (op == "~" && is(T == E)) { 512 return E(content ~ " " ~ rhs.content); 513 } else static if (op == "~") { 514 return E(content = content ~ to!string(rhs)); 515 } else { 516 static assert(0, "Operator " ~ op ~ " not implemented"); 517 } 518 } 519 520 /** Reconstruct the semantic "=" as affecting the content. 521 * 522 * Example: 523 * E("int x") = E(1) -> "x = 1" 524 */ 525 auto opAssign(T)(T rhs) pure nothrow { 526 this.content ~= " = " ~ to!string(rhs); 527 return this; 528 } 529 } 530 531 /** Code structure for generation of a C header. 532 * 533 * The content is structed as: 534 * doc 535 * header 536 * ifdef_guardbegin 537 * content 538 * ifdef_guard end 539 * 540 * Note that the indent is suppressed. 541 */ 542 struct CHModule { 543 /// Document root. 544 CModule doc; 545 /// Usually a copyright header. 546 CModule header; 547 /// Main code content. 548 CModule content; 549 550 /** 551 * Params: 552 * ifdef_guard = guard statement. 553 */ 554 this(string ifdef_guard) { 555 // Must suppress indentation to generate what is expected by the user. 556 doc = new CModule; 557 with (doc) { 558 // doc is a container of the modules so should not affect indent. 559 // header, content and footer is containers so should not affect indent. 560 // ifndef guard usually never affect indent. 561 suppressIndent(1); 562 header = base; 563 header.suppressIndent(1); 564 with (IFNDEF(ifdef_guard)) { 565 define(ifdef_guard); 566 content = base; 567 content.suppressIndent(1); 568 } 569 } 570 } 571 572 /// Render the content as a string. 573 string render() { 574 return doc.render(); 575 } 576 } 577 578 @("Test of statements") 579 unittest { 580 string expect = " 77; 581 break; 582 continue; 583 return 5; 584 return long_value; 585 goto foo; 586 bar: 587 #define foobar 588 #define smurf 1 589 "; 590 591 auto x = new CModule(); 592 593 with (x) { 594 stmt(E(77)); 595 break_; 596 continue_; 597 return_(E(5)); 598 return_("long_value"); 599 goto_("foo"); 600 label("bar"); 601 define("foobar"); 602 define("smurf", E(1)); 603 } 604 605 auto rval = x.render(); 606 assert(rval == expect, rval); 607 } 608 609 @("Test of preprocess statements") 610 unittest { 611 string expect = " #if foo 612 inside; 613 if { 614 deep inside; 615 } 616 #endif // foo 617 #ifdef bar 618 inside; 619 #endif // bar 620 #ifndef foobar 621 inside; 622 #elif wee 623 inside; 624 #else 625 inside; 626 #endif // foobar 627 "; 628 629 auto x = new CModule(); 630 631 with (x) { 632 with (IF("foo")) { 633 stmt("inside"); 634 with (suite("if")) { 635 stmt("deep inside"); 636 } 637 } 638 with (IFDEF("bar")) { 639 stmt("inside"); 640 } 641 with (IFNDEF("foobar")) { 642 stmt("inside"); 643 ELIF("wee"); 644 stmt("inside"); 645 ELSE(); 646 stmt("inside"); 647 } 648 } 649 650 auto rval = x.render(); 651 assert(rval == expect, rval); 652 } 653 654 @("Test of suites") 655 unittest { 656 string expect = " 657 foo { 658 } 659 if (foo) { 660 } 661 else if (bar) { 662 } 663 else { 664 } 665 for (x; y; z) { 666 } 667 while (x) { 668 } 669 do { 670 } while (x); 671 switch (x) { 672 } 673 case y: 674 foo; 675 default: 676 foobar; 677 int foobar(int x) { 678 } 679 int fun(int y); 680 "; 681 682 auto x = new CModule(); 683 with (x) { 684 sep(); 685 suite("foo"); 686 if_("foo"); 687 else_if("bar"); 688 else_; 689 for_("x", "y", "z"); 690 while_("x"); 691 do_while("x"); 692 switch_("x"); 693 with (case_("y")) { 694 stmt("foo"); 695 } 696 with (default_) { 697 stmt("foobar"); 698 } 699 func_body("int", "foobar", "int x"); 700 func("int", "fun", "int y"); 701 } 702 703 auto rval = x.render; 704 assert(rval == expect, rval); 705 } 706 707 @("Test of complicated switch") 708 unittest { 709 string expect = " 710 switch (x) { 711 case 0: 712 return 5; 713 break; 714 case 1: 715 return 3; 716 break; 717 default: 718 return -1; 719 } 720 "; 721 722 auto x = new CModule(); 723 with (x) { 724 sep(); 725 with (switch_("x")) { 726 with (case_(E(0))) { 727 return_(E(5)); 728 break_; 729 } 730 with (case_(E(1))) { 731 return_(E(3)); 732 break_; 733 } 734 with (default_) { 735 return_(E(-1)); 736 } 737 } 738 } 739 740 auto rval = x.render; 741 assert(rval == expect, rval); 742 } 743 744 @("Test of empty CSuite") 745 unittest { 746 auto x = new Suite!CModule("test"); 747 assert(x.render == "test {\n}", x.render); 748 } 749 750 @("Test of stmt_append_end") 751 unittest { 752 string[string] attrs; 753 string stmt = "some_line"; 754 string result = stmt_append_end(stmt, attrs); 755 assert(stmt ~ ";" == result, result); 756 757 result = stmt_append_end(stmt ~ ";", attrs); 758 assert(stmt ~ ";" == result, result); 759 760 attrs["end"] = "{"; 761 result = stmt_append_end(stmt, attrs); 762 assert(stmt ~ "{" == result, result); 763 } 764 765 @("Test of CSuite with formatting") 766 unittest { 767 auto x = new Suite!CModule("if (x > 5)"); 768 assert(x.render() == "if (x > 5) {\n}", x.render); 769 } 770 771 @("Test of CSuite with simple text") 772 unittest { 773 // also test that text(..) do NOT add a linebreak 774 auto x = new Suite!CModule("foo"); 775 with (x) { 776 text("bar"); 777 } 778 assert(x.render() == "foo {\nbar}", x.render); 779 } 780 781 @("Test of CSuite with simple text and changed begin") 782 unittest { 783 auto x = new Suite!CModule("foo"); 784 with (x[$.begin = "_:_"]) { 785 text("bar"); 786 } 787 assert(x.render() == "foo_:_bar}", x.render); 788 } 789 790 @("Test of CSuite with simple text and changed end") 791 unittest { 792 auto x = new Suite!CModule("foo"); 793 with (x[$.end = "_:_"]) { 794 text("bar"); 795 } 796 assert(x.render() == "foo {\nbar_:_", x.render); 797 } 798 799 @("Test of nested CSuite") 800 unittest { 801 auto x = new Suite!CModule("foo"); 802 with (x) { 803 text("bar"); 804 sep(); 805 with (suite("smurf")) { 806 comment("bar"); 807 } 808 } 809 assert(x.render() == "foo { 810 bar 811 smurf { 812 // bar 813 } 814 }", x.render); 815 } 816 817 @("Test of text in CModule with guard") 818 unittest { 819 auto hdr = CHModule("somefile_hpp"); 820 821 with (hdr.header) { 822 text("header text"); 823 sep(); 824 comment("header comment"); 825 } 826 with (hdr.content) { 827 text("content text"); 828 sep(); 829 comment("content comment"); 830 } 831 832 assert(hdr.render == "header text 833 // header comment 834 #ifndef somefile_hpp 835 #define somefile_hpp 836 content text 837 // content comment 838 #endif // somefile_hpp 839 ", hdr.render); 840 } 841 842 @("Test of Expression. Type conversion") 843 unittest { 844 import std.conv : to; 845 846 string implicit = E("foo")(77); 847 assert("foo(77)" == implicit, implicit); 848 849 auto explicit = cast(string) E("foo")(77); 850 assert("foo(77)" == explicit, explicit); 851 852 auto to_string = to!string(E("foo")(77)); 853 assert("foo(77)" == to_string, to_string); 854 } 855 856 @("Test of Expression") 857 unittest { 858 string expect = "foo 859 foo(77) 860 77 + 3 861 77 - 3 862 44 - 3 + 7 863 (44 - 3 + 7) 864 foo(42 + 43) 865 int x = 7 866 "; 867 auto x = new CModule(); 868 x.suppressIndent(1); 869 870 x.text("foo"); 871 x.sep; 872 x.text(E("foo")(77)); 873 x.sep; 874 x.text(E(77) + 3); 875 x.sep; 876 x.text(E(77) - 3); 877 x.sep; 878 x.text(E(44) - E(3) + E(7)); 879 x.sep; 880 x.text(E()(E(44) - E(3) + E(7))); 881 x.sep; 882 x.text(E("foo")(E(42) + 43)); 883 x.sep; 884 x.text(E("int x") = 7); 885 x.sep; 886 887 auto rval = x.render; 888 assert(rval == expect, rval); 889 } 890 891 @("Test of indent") 892 unittest { 893 string expect = " L2 1 { 894 L3 1.1 { 895 } 896 L3 1.2 { 897 L4 1.2.1 { 898 } 899 } 900 } 901 "; 902 903 auto x = new CModule(); 904 905 with (x) { 906 with (suite("L2 1")) { 907 suite("L3 1.1"); 908 with (suite("L3 1.2")) { 909 suite("L4 1.2.1"); 910 } 911 } 912 } 913 914 auto rval = x.render(); 915 assert(rval == expect, rval); 916 } 917 918 @("Test of single suppressing of indent") 919 unittest { 920 string expect = "L1 1 { 921 L1 1.1 { 922 } 923 L1 1.2 { 924 L2 1.2.1 { 925 } 926 } 927 } 928 "; 929 930 auto x = new CModule(); 931 932 with (x) { 933 suppressIndent(1); 934 with (suite("L1 1")) { 935 suite("L1 1.1"); 936 with (suite("L1 1.2")) { 937 suite("L2 1.2.1"); 938 } 939 } 940 } 941 942 auto rval = x.render(); 943 assert(rval == expect, rval); 944 } 945 946 @("Test of nested suppressing of indent") 947 unittest { 948 string expect = "L1 1 { 949 L1 1.1 { 950 } 951 L1 1.2 { 952 L1 1.2.1 { 953 L2 1.2.1.1 { 954 } 955 } 956 } 957 } 958 "; 959 960 auto x = new CModule(); 961 962 with (x) { 963 suppressIndent(1); 964 // suppressing L1 1 to be on the same level as x 965 // affects L1 1 and the first level of children 966 with (suite("L1 1")) { 967 suite("L1 1.1"); // suppressed 968 with (suite("L1 1.2")) { 969 suppressIndent(1); 970 with (suite("L1 1.2.1")) { // suppressed 971 suite("L2 1.2.1.1"); 972 } 973 } 974 } 975 } 976 977 auto rval = x.render(); 978 assert(rval == expect, rval); 979 } 980 981 @("shall be an expression assignment") 982 unittest { 983 auto expect = " a = p; 984 "; 985 986 auto m = new CModule; 987 auto e = E("a"); 988 e = E("p"); 989 m.stmt(e); 990 991 assert(expect == m.render, m.render); 992 } 993 994 @("shall be a return with and without value") 995 unittest { 996 auto expect = " return; 997 return a; 998 "; 999 1000 auto m = new CModule; 1001 m.return_(); 1002 m.return_("a"); 1003 1004 assert(expect == m.render, m.render); 1005 } 1006 1007 @("shall be a C enum definition") 1008 unittest { 1009 auto expect = " enum { 1010 } 1011 enum A { 1012 }; 1013 enum B { 1014 L0, 1015 L1 = 2, 1016 } 1017 "; 1018 1019 auto m = new CModule; 1020 m.enum_; 1021 m.enum_("A"); 1022 with (m.enum_("B")) { 1023 enum_const("L0"); 1024 enum_const(E("L1") = E("2")); 1025 } 1026 } 1027 1028 @("shall be an extern var") 1029 unittest { 1030 auto expect = ` extern var; 1031 `; 1032 auto m = new CModule; 1033 m.extern_decl("var"); 1034 1035 assert(expect == m.render, m.render); 1036 } 1037 1038 @("shall be an extern C function") 1039 unittest { 1040 auto expect = ` extern "C" void f(); 1041 `; 1042 auto m = new CModule; 1043 m.extern_("C").func("void", "f"); 1044 1045 assert(expect == m.render, m.render); 1046 }