1 module tooling.CxxImplement; 2 3 import std.algorithm; 4 import std.array; 5 import std.conv; 6 import std.exception; 7 import std.file; 8 import std.range; 9 import std.stdio; 10 11 import tooling.Scanner; 12 import tooling.TreeRange; 13 import tooling.Tokenizer; 14 import tooling.TokenRange; 15 import tooling.MergedRange; 16 import tooling.UnifyRange; 17 18 // Implement the cpp file to a given header 19 20 string createNamespace(Entity entity, string indent) 21 { 22 return (indent ~ "namespace " ~ entity.name ~ "\n" ~ 23 indent ~ "{\n" ~ 24 createCxxFileContent(entity.content_, indent ~ " ", []) ~ 25 indent ~ "}\n" 26 ); 27 } 28 29 string createClass(Entity entity, string indent, string[] classStack) 30 { 31 return createCxxFileContent(entity.content_, indent, classStack ~ entity.name); 32 } 33 34 string createFunction(Entity entity, string indent, string[] classStack) 35 { 36 auto className = classStack.joiner("::").to!string; 37 if (className) className ~= "::"; 38 bool headerOnly(string name) { 39 foreach (w; ["explicit", "static", "override", "virtual"]) { 40 if (name == w) return true; 41 } 42 return false; 43 } 44 auto tokens = entity.expr_.filter!(t => !headerOnly(t.value)).array; 45 string result; 46 if (tokens) { 47 result ~= indent; 48 bool classNameInserted = false; 49 if (classStack) { 50 if (tokens[0].value == "~" || tokens[0].value == entity.name_) { 51 result ~= className; 52 classNameInserted = true; 53 } 54 } 55 result ~= tokens[0].value; 56 foreach (t; tokens[1 .. $]) { 57 if (!classNameInserted && (t.value == "~" || t.value == entity.name_)) { 58 result ~= t.precedingWhitespace_ ~ className ~ t.value; 59 classNameInserted = true; 60 } 61 else { 62 result ~= t.precedingWhitespace_ ~ t.value; 63 } 64 } 65 result ~= "\n" ~ 66 indent ~ "{\n" ~ 67 indent ~ " // FIXME: Implementation missing\n" ~ 68 indent ~ "}\n\n"; 69 } 70 return result; 71 } 72 73 string createCxxFileContent(Entity[] entities, string indent, string[] classStack) 74 { 75 string result; 76 foreach (entity; entities) { 77 switch (entity.type_) { 78 case "namespace": 79 result ~= createNamespace(entity, indent); 80 break; 81 case "class": 82 result ~= createClass(entity, indent, classStack); 83 break; 84 case "function": 85 if (!isInline(entity)) { 86 result ~= createFunction(entity, indent, classStack); 87 } 88 break; 89 default: 90 break; 91 } 92 } 93 return result; 94 } 95 96 void implement(string headerFileName, string sourceFileName) 97 { 98 auto headerTokens = readTokens(headerFileName); 99 auto entities = scanTokens(headerTokens); 100 101 auto content = createCxxFileContent(entities, "", []); 102 103 Token[] tokens; 104 tokenize(content, sourceFileName, tokens); 105 106 if (!sourceFileName.empty) 107 { 108 auto sourceTokens = readTokens(sourceFileName); 109 110 // merge with preference to source tokens if available 111 auto mergedTokens = mergedRange([sourceTokens, tokens[0 .. $-1]]).array; 112 tokens = unifyFunctionsRange(mergedTokens).array; 113 } 114 115 auto outfile = sourceFileName == "-" ? stdout : File(sourceFileName, "w"); 116 outfile.writeTokens(tokens); 117 outfile.flush; 118 } 119 120 int implementMain(string[] args) 121 { 122 string name = args[0]; 123 string cmd = args[1]; 124 string infileName; 125 string outfileName; 126 127 args = args[2 .. $]; 128 129 if (args.length > 1) 130 { 131 if (args[0] == "-o") 132 { 133 outfileName = args[1]; 134 args = args[2 .. $]; 135 } 136 } 137 138 if (!args.length) 139 { 140 writeln("Usage: ", name, " ", cmd, " [-o outfile] inputfile"); 141 return 1; 142 } 143 144 infileName = args[0]; 145 if (infileName != "-" && outfileName.empty) 146 { 147 outfileName = infileName.splitter(".").array[0 .. $-1].joiner(".").to!string ~ ".cpp"; 148 } 149 150 implement(infileName, outfileName); 151 return 0; 152 }