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 } catch (Exception e) { 366 } 367 368 if (!in_pattern && s[0] != '#') { 369 string end = ";"; 370 if (auto v = "end" in attrs) { 371 end = *v; 372 } 373 s ~= end; 374 } 375 } 376 377 return s; 378 } 379 380 /** Affected by attribute end. 381 * stmt ~ end 382 * <recursive> 383 */ 384 class Stmt(T) : T { 385 private string headline; 386 387 /// Content of the statement. 388 this(string headline) { 389 this.headline = headline; 390 } 391 392 override string renderIndent(int parent_level, int level) { 393 string r = stmt_append_end(headline, attrs); 394 395 if ("noindent" !in attrs) { 396 r = indent(r, parent_level, level); 397 } 398 399 return r; 400 } 401 } 402 403 /** Affected by attribute begin, end, noindent. 404 * headline ~ begin 405 * <recursive> 406 * end 407 * noindent affects post_recursive. If set no indention there. 408 * r.length > 0 catches the case when begin or end is empty string. Used in switch/case. 409 */ 410 class Suite(T) : T { 411 private string headline; 412 413 /// Content of the suite/block. 414 this(string headline) { 415 this.headline = headline; 416 } 417 418 override string renderIndent(int parent_level, int level) { 419 import std.ascii : newline; 420 421 string r = headline ~ " {" ~ newline; 422 if (auto v = "begin" in attrs) { 423 r = headline ~ *v; 424 } 425 426 if (r.length > 0 && !("noindent" in attrs)) { 427 r = indent(r, parent_level, level); 428 } 429 return r; 430 } 431 432 override string renderPostRecursive(int parent_level, int level) { 433 string r = "}"; 434 if (auto v = "end" in attrs) { 435 r = *v; 436 } 437 438 if (r.length > 0 && "noindent" !in attrs) { 439 r = indent(r, parent_level, level); 440 } 441 return r; 442 } 443 } 444 445 /// An expressioin in C. 446 struct E { 447 @safe pure: 448 import std.conv : to; 449 450 private string content; 451 452 /// Content of the expression. 453 this(string content) nothrow pure { 454 this.content = content; 455 } 456 457 /// Convert argument via std.conv.to!string. 458 this(T)(T content) nothrow pure { 459 this.content = to!string(content); 460 } 461 462 /// Concatenate two expressions with ".". 463 this(E lhs, string rhs) nothrow pure { 464 this.content = lhs.content ~ "." ~ rhs; 465 } 466 467 /// ditto 468 auto e(string lhs) nothrow pure const { 469 return E(content ~ "." ~ lhs); 470 } 471 472 /// ditto 473 auto e(E lhs) nothrow pure const { 474 return E(content ~ "." ~ lhs.content); 475 } 476 477 /// Represent the semantic function call. 478 auto opCall(T...)(auto ref T value) pure const { 479 return E(content ~ "(" ~ paramsToString(value) ~ ")"); 480 } 481 482 // implicit 483 @property string toString() pure const nothrow { 484 return content; 485 } 486 487 alias toString this; 488 489 /// String representation of the content. Explicit cast. 490 T opCast(T : string)() pure const nothrow { 491 return content; 492 } 493 494 /// Preprend the textual representation of the operator to the content. 495 auto opUnary(string op)() pure nothrow const { 496 static if (op == "+" || op == "-" || op == "*" || op == "++" || op == "--") { 497 return E(mixin("\"" ~ op ~ "\"~content")); 498 } else { 499 static assert(0, "Operator " ~ op ~ " not implemented"); 500 } 501 } 502 503 /** Represent the semantic meaning of binary operators. 504 * 505 * ~ is special cased but OK for it doesn't exist in C/C++. 506 */ 507 auto opBinary(string op, T)(in T rhs) pure nothrow const { 508 static if (op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op == "&") { 509 return E(mixin("content~\" " ~ op ~ " \"~to!string(rhs)")); 510 } else static if (op == "~" && is(T == E)) { 511 return E(content ~ " " ~ rhs.content); 512 } else static if (op == "~") { 513 return E(content = content ~ to!string(rhs)); 514 } else { 515 static assert(0, "Operator " ~ op ~ " not implemented"); 516 } 517 } 518 519 /** Reconstruct the semantic "=" as affecting the content. 520 * 521 * Example: 522 * E("int x") = E(1) -> "x = 1" 523 */ 524 auto opAssign(T)(T rhs) pure nothrow { 525 this.content ~= " = " ~ to!string(rhs); 526 return this; 527 } 528 } 529 530 /** Code structure for generation of a C header. 531 * 532 * The content is structed as: 533 * doc 534 * header 535 * ifdef_guardbegin 536 * content 537 * ifdef_guard end 538 * 539 * Note that the indent is suppressed. 540 */ 541 struct CHModule { 542 /// Document root. 543 CModule doc; 544 /// Usually a copyright header. 545 CModule header; 546 /// Main code content. 547 CModule content; 548 549 /** 550 * Params: 551 * ifdef_guard = guard statement. 552 */ 553 this(string ifdef_guard) { 554 // Must suppress indentation to generate what is expected by the user. 555 doc = new CModule; 556 with (doc) { 557 // doc is a container of the modules so should not affect indent. 558 // header, content and footer is containers so should not affect indent. 559 // ifndef guard usually never affect indent. 560 suppressIndent(1); 561 header = base; 562 header.suppressIndent(1); 563 with (IFNDEF(ifdef_guard)) { 564 define(ifdef_guard); 565 content = base; 566 content.suppressIndent(1); 567 } 568 } 569 } 570 571 /// Render the content as a string. 572 string render() { 573 return doc.render(); 574 } 575 } 576 577 @("Test of statements") 578 unittest { 579 string expect = " 77; 580 break; 581 continue; 582 return 5; 583 return long_value; 584 goto foo; 585 bar: 586 #define foobar 587 #define smurf 1 588 "; 589 590 auto x = new CModule(); 591 592 with (x) { 593 stmt(E(77)); 594 break_; 595 continue_; 596 return_(E(5)); 597 return_("long_value"); 598 goto_("foo"); 599 label("bar"); 600 define("foobar"); 601 define("smurf", E(1)); 602 } 603 604 auto rval = x.render(); 605 assert(rval == expect, rval); 606 } 607 608 @("Test of preprocess statements") 609 unittest { 610 string expect = " #if foo 611 inside; 612 if { 613 deep inside; 614 } 615 #endif // foo 616 #ifdef bar 617 inside; 618 #endif // bar 619 #ifndef foobar 620 inside; 621 #elif wee 622 inside; 623 #else 624 inside; 625 #endif // foobar 626 "; 627 628 auto x = new CModule(); 629 630 with (x) { 631 with (IF("foo")) { 632 stmt("inside"); 633 with (suite("if")) { 634 stmt("deep inside"); 635 } 636 } 637 with (IFDEF("bar")) { 638 stmt("inside"); 639 } 640 with (IFNDEF("foobar")) { 641 stmt("inside"); 642 ELIF("wee"); 643 stmt("inside"); 644 ELSE(); 645 stmt("inside"); 646 } 647 } 648 649 auto rval = x.render(); 650 assert(rval == expect, rval); 651 } 652 653 @("Test of suites") 654 unittest { 655 string expect = " 656 foo { 657 } 658 if (foo) { 659 } 660 else if (bar) { 661 } 662 else { 663 } 664 for (x; y; z) { 665 } 666 while (x) { 667 } 668 do { 669 } while (x); 670 switch (x) { 671 } 672 case y: 673 foo; 674 default: 675 foobar; 676 int foobar(int x) { 677 } 678 int fun(int y); 679 "; 680 681 auto x = new CModule(); 682 with (x) { 683 sep(); 684 suite("foo"); 685 if_("foo"); 686 else_if("bar"); 687 else_; 688 for_("x", "y", "z"); 689 while_("x"); 690 do_while("x"); 691 switch_("x"); 692 with (case_("y")) { 693 stmt("foo"); 694 } 695 with (default_) { 696 stmt("foobar"); 697 } 698 func_body("int", "foobar", "int x"); 699 func("int", "fun", "int y"); 700 } 701 702 auto rval = x.render; 703 assert(rval == expect, rval); 704 } 705 706 @("Test of complicated switch") 707 unittest { 708 string expect = " 709 switch (x) { 710 case 0: 711 return 5; 712 break; 713 case 1: 714 return 3; 715 break; 716 default: 717 return -1; 718 } 719 "; 720 721 auto x = new CModule(); 722 with (x) { 723 sep(); 724 with (switch_("x")) { 725 with (case_(E(0))) { 726 return_(E(5)); 727 break_; 728 } 729 with (case_(E(1))) { 730 return_(E(3)); 731 break_; 732 } 733 with (default_) { 734 return_(E(-1)); 735 } 736 } 737 } 738 739 auto rval = x.render; 740 assert(rval == expect, rval); 741 } 742 743 @("Test of empty CSuite") 744 unittest { 745 auto x = new Suite!CModule("test"); 746 assert(x.render == "test {\n}", x.render); 747 } 748 749 @("Test of stmt_append_end") 750 unittest { 751 string[string] attrs; 752 string stmt = "some_line"; 753 string result = stmt_append_end(stmt, attrs); 754 assert(stmt ~ ";" == result, result); 755 756 result = stmt_append_end(stmt ~ ";", attrs); 757 assert(stmt ~ ";" == result, result); 758 759 attrs["end"] = "{"; 760 result = stmt_append_end(stmt, attrs); 761 assert(stmt ~ "{" == result, result); 762 } 763 764 @("Test of CSuite with formatting") 765 unittest { 766 auto x = new Suite!CModule("if (x > 5)"); 767 assert(x.render() == "if (x > 5) {\n}", x.render); 768 } 769 770 @("Test of CSuite with simple text") 771 unittest { 772 // also test that text(..) do NOT add a linebreak 773 auto x = new Suite!CModule("foo"); 774 with (x) { 775 text("bar"); 776 } 777 assert(x.render() == "foo {\nbar}", x.render); 778 } 779 780 @("Test of CSuite with simple text and changed begin") 781 unittest { 782 auto x = new Suite!CModule("foo"); 783 with (x[$.begin = "_:_"]) { 784 text("bar"); 785 } 786 assert(x.render() == "foo_:_bar}", x.render); 787 } 788 789 @("Test of CSuite with simple text and changed end") 790 unittest { 791 auto x = new Suite!CModule("foo"); 792 with (x[$.end = "_:_"]) { 793 text("bar"); 794 } 795 assert(x.render() == "foo {\nbar_:_", x.render); 796 } 797 798 @("Test of nested CSuite") 799 unittest { 800 auto x = new Suite!CModule("foo"); 801 with (x) { 802 text("bar"); 803 sep(); 804 with (suite("smurf")) { 805 comment("bar"); 806 } 807 } 808 assert(x.render() == "foo { 809 bar 810 smurf { 811 // bar 812 } 813 }", x.render); 814 } 815 816 @("Test of text in CModule with guard") 817 unittest { 818 auto hdr = CHModule("somefile_hpp"); 819 820 with (hdr.header) { 821 text("header text"); 822 sep(); 823 comment("header comment"); 824 } 825 with (hdr.content) { 826 text("content text"); 827 sep(); 828 comment("content comment"); 829 } 830 831 assert(hdr.render == "header text 832 // header comment 833 #ifndef somefile_hpp 834 #define somefile_hpp 835 content text 836 // content comment 837 #endif // somefile_hpp 838 ", hdr.render); 839 } 840 841 @("Test of Expression. Type conversion") 842 unittest { 843 import std.conv : to; 844 845 string implicit = E("foo")(77); 846 assert("foo(77)" == implicit, implicit); 847 848 auto explicit = cast(string) E("foo")(77); 849 assert("foo(77)" == explicit, explicit); 850 851 auto to_string = to!string(E("foo")(77)); 852 assert("foo(77)" == to_string, to_string); 853 } 854 855 @("Test of Expression") 856 unittest { 857 string expect = "foo 858 foo(77) 859 77 + 3 860 77 - 3 861 44 - 3 + 7 862 (44 - 3 + 7) 863 foo(42 + 43) 864 int x = 7 865 "; 866 auto x = new CModule(); 867 x.suppressIndent(1); 868 869 x.text("foo"); 870 x.sep; 871 x.text(E("foo")(77)); 872 x.sep; 873 x.text(E(77) + 3); 874 x.sep; 875 x.text(E(77) - 3); 876 x.sep; 877 x.text(E(44) - E(3) + E(7)); 878 x.sep; 879 x.text(E()(E(44) - E(3) + E(7))); 880 x.sep; 881 x.text(E("foo")(E(42) + 43)); 882 x.sep; 883 x.text(E("int x") = 7); 884 x.sep; 885 886 auto rval = x.render; 887 assert(rval == expect, rval); 888 } 889 890 @("Test of indent") 891 unittest { 892 string expect = " L2 1 { 893 L3 1.1 { 894 } 895 L3 1.2 { 896 L4 1.2.1 { 897 } 898 } 899 } 900 "; 901 902 auto x = new CModule(); 903 904 with (x) { 905 with (suite("L2 1")) { 906 suite("L3 1.1"); 907 with (suite("L3 1.2")) { 908 suite("L4 1.2.1"); 909 } 910 } 911 } 912 913 auto rval = x.render(); 914 assert(rval == expect, rval); 915 } 916 917 @("Test of single suppressing of indent") 918 unittest { 919 string expect = "L1 1 { 920 L1 1.1 { 921 } 922 L1 1.2 { 923 L2 1.2.1 { 924 } 925 } 926 } 927 "; 928 929 auto x = new CModule(); 930 931 with (x) { 932 suppressIndent(1); 933 with (suite("L1 1")) { 934 suite("L1 1.1"); 935 with (suite("L1 1.2")) { 936 suite("L2 1.2.1"); 937 } 938 } 939 } 940 941 auto rval = x.render(); 942 assert(rval == expect, rval); 943 } 944 945 @("Test of nested suppressing of indent") 946 unittest { 947 string expect = "L1 1 { 948 L1 1.1 { 949 } 950 L1 1.2 { 951 L1 1.2.1 { 952 L2 1.2.1.1 { 953 } 954 } 955 } 956 } 957 "; 958 959 auto x = new CModule(); 960 961 with (x) { 962 suppressIndent(1); 963 // suppressing L1 1 to be on the same level as x 964 // affects L1 1 and the first level of children 965 with (suite("L1 1")) { 966 suite("L1 1.1"); // suppressed 967 with (suite("L1 1.2")) { 968 suppressIndent(1); 969 with (suite("L1 1.2.1")) { // suppressed 970 suite("L2 1.2.1.1"); 971 } 972 } 973 } 974 } 975 976 auto rval = x.render(); 977 assert(rval == expect, rval); 978 } 979 980 @("shall be an expression assignment") 981 unittest { 982 auto expect = " a = p; 983 "; 984 985 auto m = new CModule; 986 auto e = E("a"); 987 e = E("p"); 988 m.stmt(e); 989 990 assert(expect == m.render, m.render); 991 } 992 993 @("shall be a return with and without value") 994 unittest { 995 auto expect = " return; 996 return a; 997 "; 998 999 auto m = new CModule; 1000 m.return_(); 1001 m.return_("a"); 1002 1003 assert(expect == m.render, m.render); 1004 } 1005 1006 @("shall be a C enum definition") 1007 unittest { 1008 auto expect = " enum { 1009 } 1010 enum A { 1011 }; 1012 enum B { 1013 L0, 1014 L1 = 2, 1015 } 1016 "; 1017 1018 auto m = new CModule; 1019 m.enum_; 1020 m.enum_("A"); 1021 with (m.enum_("B")) { 1022 enum_const("L0"); 1023 enum_const(E("L1") = E("2")); 1024 } 1025 } 1026 1027 @("shall be an extern var") 1028 unittest { 1029 auto expect = ` extern var; 1030 `; 1031 auto m = new CModule; 1032 m.extern_decl("var"); 1033 1034 assert(expect == m.render, m.render); 1035 } 1036 1037 @("shall be an extern C function") 1038 unittest { 1039 auto expect = ` extern "C" void f(); 1040 `; 1041 auto m = new CModule; 1042 m.extern_("C").func("void", "f"); 1043 1044 assert(expect == m.render, m.render); 1045 }