1 module overloadgen;
2 import plugin;
3 import std.stdio;
4 import std.json;
5 import std.array : split;
6 import std.algorithm : countUntil;
7 import std.file : readText, exists;
8 
9 bool willDefineOverloadTemplate = false;
10 
11 static struct OverloadedFunction
12 {
13     string returnType;
14     string argsTypes;
15     string argsParams;
16     string cimguiFunc;
17 }
18 
19 static struct Function
20 {
21     string owner;
22     string name;
23     bool exists;
24     bool willForward;
25 
26     @property string fname()
27     {
28         if(owner == "")
29             return name;
30         else
31             return owner~"_"~name;
32     }
33     OverloadedFunction[] overloads;
34 }
35 
36 string[] ignoreList =
37 [
38     "ImVector_ImVector",
39     "ImVector_back",
40     "ImVector_begin",
41     "ImVector_end",
42     "ImVector_erase",
43     "ImVector_find",
44     "ImVector_front",
45     "ImVector_resize"
46 ];
47 
48 bool isOnIgnoreList(string line)
49 {
50     foreach(ignore;ignoreList)
51         if(line.countUntil(ignore) != -1)
52             return true;
53     return false;
54 }
55 
56 bool hasVarArgs(string params)
57 {
58     return params.countUntil("...") != -1;
59 }
60 
61 static Function[] getFunctions(File file)
62 {
63     Function[] ret;
64     ret.reserve(128);
65 
66     Function func;
67     foreach(int i, string line; lines(file))
68     {
69         int firstCharValue = line[0];
70         if(firstCharValue == '-')
71             continue;
72         if(firstCharValue >= '0' && firstCharValue <= '9' && func.exists)
73         {
74             string[] infos = line.split();
75             if(infos[1] == "overloaded")
76                 return ret~= func;
77             else
78             {
79                 OverloadedFunction overload;
80                 if(infos[1] == "nil")
81                     overload.returnType = func.owner;
82                 else
83                     overload.returnType = infos[1];
84                 long constInd = infos.countUntil("const");
85                 int infoIndex = 2;
86                 if(constInd != -1)
87                 {
88                     overload.returnType~= " "~infos[constInd+1];
89                     infoIndex++;
90                 }
91                 overload.cimguiFunc = infos[infoIndex];
92                 
93                 // long argsStart = line.countUntil("("); //Not used anymore
94                 getParameters(func, overload);
95                 // overload.argsTypes = line[cast(uint)argsStart..line.length];
96                 func.overloads~= overload;
97             }
98         }
99         else
100         {
101             if(func.exists)
102                 ret~= func;
103             func = Function();
104             string[] infos = line.split("_"); //Gets the function name
105             if(isOnIgnoreList(line))
106             {
107                 func.exists = false;
108                 continue;
109             }
110             int nameIndex = 0;
111             if(infos.length != 1)
112             {
113                 func.owner = infos[0];
114                 nameIndex = 1;
115                 long separatorIndex = infos[nameIndex].countUntil("\t");
116                 if(separatorIndex == -1)
117                     separatorIndex = infos[nameIndex].countUntil(" ");
118                 if(separatorIndex == -1)
119                     return null;
120                 func.name = infos[nameIndex][0..cast(uint)separatorIndex];
121             }
122             else
123             {
124                 infos = line.split();
125                 func.owner = "";
126                 func.name = infos[0];
127             }
128             
129             
130             func.exists = true;
131         }
132     }
133     return ret;
134 }
135 
136 string generateAliasOverload()
137 {
138 return q{
139 static template overload(Func...)
140 {
141     import std.traits;
142     static foreach(f; Funcs)
143         auto overload(Parameters!f params){
144             return f(params);}
145 }};
146 }
147 
148 static void getParameters(ref Function func, ref OverloadedFunction overload)
149 {
150     JSONValue jsonFunc = defs[func.fname].array;
151     foreach (JSONValue ovFunc; jsonFunc.array)
152     {
153         if(ovFunc["ov_cimguiname"].str == overload.cimguiFunc)
154         {
155 
156             overload.argsTypes = ovFunc["argsoriginal"].str;
157             if(hasVarArgs(overload.argsTypes))
158             {
159                 func.willForward = true;
160             }
161             overload.argsParams = "(";
162             JSONValue argParams = ovFunc["argsT"];
163             size_t len = argParams.array.length;
164             foreach(i, params; argParams.array)
165             {
166                 overload.argsParams~= params["name"].str;
167                 if(i+1 != len)
168                     overload.argsParams~=",";
169             }
170             overload.argsParams~= ")";
171             break;
172         }
173     }
174 }
175 
176 static string generateOverloads(Function[] funcs)
177 {
178     string fileContent = "";
179     string line = "";
180     import std.format:format;
181 
182     string funcName;
183     foreach(func; funcs)
184     {
185         funcName = func.name;
186         if(func.willForward)
187         {
188             if(!willDefineOverloadTemplate)
189                 willDefineOverloadTemplate = true;
190             line = "alias "~funcName ~"= overload!(";
191             string comment = "/**\n";
192             foreach(i, ov ; func.overloads)
193             {
194                 comment~= "*\t"~ov.returnType~" "~funcName~ov.argsTypes~"\n";
195                 line~= ov.cimguiFunc;
196                 if(i != func.overloads.length - 1)
197                     line~=",";
198                 //Write a comment on the alias for appearing 
199             }
200             comment~="*/\n";
201             fileContent~= comment~line~");\n";
202         }
203         else foreach(overload; func.overloads)
204         {
205             line = overload.returnType~" "~funcName~overload.argsTypes~"{"~overload.cimguiFunc;
206             line~= overload.argsParams~";}";
207             fileContent~= line~"\n";
208         }
209     }
210     return fileContent;
211 }
212 
213 
214 static JSONValue defs = null;
215 //Will only receive the path to overloads.txt
216 
217 string getOverloadsPath(string cimguiPath)
218 {
219     return cimguiPath~"/generator/output/overloads.txt";
220 }
221 string getDefinitionsPath(string cimguiPath)
222 {
223     return cimguiPath~"/generator/output/definitions.json";
224 }
225 
226 
227 class CimGuiOverloadPlugin : Plugin
228 {
229     string storedStr;
230     override string target(){return "cimgui-overloads";}
231     override string convertToD_Pipe()
232     {
233         return storedStr;
234     }
235     string outputPath;
236     override int main(string[] args)
237     {
238         
239         if(args.length < 2)
240             return returnError("Argument Expected:\nNo path for cimgui provided!");
241         else if(args.length == 3)
242             outputPath = args[2];
243         string cimguiPath = args[1];
244         if(!exists(cimguiPath))
245             return returnError("Cimgui directory '"~cimguiPath~"' not found");
246         string overloads = getOverloadsPath(cimguiPath);
247         string defsPath = getDefinitionsPath(cimguiPath);
248 
249         if(!exists(overloads))
250             return returnError("Overloads path '"~overloads~"' does not exists");
251         if(!exists(defsPath))
252             return returnError("Definitions path '"~defsPath~"' does not exists");
253         
254         defs = parseJSON(readText(defsPath));
255 
256         File f = File(overloads);
257         Function[] funcs = getFunctions(f);
258         storedStr = generateOverloads(funcs);
259 
260         return Plugin.SUCCESS;
261     }
262     override int onReturnControl(string processedStr)
263     {
264         import std.file : write;
265         string s = "module bindbc.cimgui.overloads;\n\n";
266         s~= "import bindbc.cimgui.funcs;\n";
267         if(willDefineOverloadTemplate)
268             s~= generateAliasOverload();
269         s~="\n";
270         
271         if(outputPath)
272         {
273             if(outputPath[$-1] == "/")
274                 outputPath = outputPath[0..$-1];
275             write(outputPath~"/overloads.d", s~processedStr);
276         }
277         else
278             write("overloads.d", s~processedStr);
279         return Plugin.SUCCESS;
280     }
281     override string getHelpInformation()
282     {
283         return r"This plugin was made to be used in conjunction with BindBC-Generator, located on
284 https://github.com/MrcSnm/bindbc-generator
285 
286 1: Argument must be 'cimgui' path, it will look for definitions.json and overloads.txt 
287 2(Optional): Output path";
288     }
289 
290 }
291 
292 extern(C) export Plugin exportOverloadgen()
293 {
294     return new CimGuiOverloadPlugin();
295 }