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 }