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