1 /**
2  * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: Oct 6, 2011
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module dstep.translator.Translator;
8 
9 import std.file;
10 
11 import mambo.core._;
12 
13 import clang.c.Index;
14 import clang.Cursor;
15 import clang.File;
16 import clang.TranslationUnit;
17 import clang.Type;
18 import clang.Util;
19 
20 import dstep.translator.Declaration;
21 import dstep.translator.Enum;
22 import dstep.translator.IncludeHandler;
23 import dstep.translator.objc.Category;
24 import dstep.translator.objc.ObjcInterface;
25 import dstep.translator.Output;
26 import dstep.translator.Record;
27 import dstep.translator.Type;
28 
29 private static string[Cursor] anonymousNames;
30 
31 class Translator
32 {
33     static struct Options
34     {
35         string outputFile;
36         Language language = Language.c;
37     }
38 
39     private
40     {
41         TranslationUnit translationUnit;
42 
43         string outputFile;
44         string inputFilename;
45         File inputFile;
46         Language language;
47         string[string] deferredDeclarations;
48     }
49 
50     this (string inputFilename, TranslationUnit translationUnit, const Options options = Options.init)
51     {
52         this.inputFilename = inputFilename;
53         this.translationUnit = translationUnit;
54         outputFile = options.outputFile;
55         language = options.language;
56 
57         inputFile = translationUnit.file(inputFilename);
58     }
59 
60     void translate ()
61     {
62         foreach (cursor, parent ; translationUnit.declarations)
63         {
64             if (skipDeclaration(cursor))
65                 continue;
66 
67             output.newContext();
68             auto code = translate(cursor, parent);
69 
70             with (CXCursorKind)
71                 switch (cursor.kind)
72                 {
73                     case CXCursor_ObjCInterfaceDecl:
74                     case CXCursor_ObjCProtocolDecl:
75                     case CXCursor_ObjCCategoryDecl:
76                         output.classes ~= code;
77                     break;
78 
79                     case CXCursor_StructDecl:
80                         if (cursor.isDefinition)
81                             output.structs ~= code;
82                         break;
83                     case CXCursor_EnumDecl: output.enums ~= code; break;
84                     case CXCursor_UnionDecl: output.unions ~= code; break;
85                     case CXCursor_VarDecl: output.variables ~= code; break;
86                     case CXCursor_FunctionDecl: output.functions ~= code; break;
87                     case CXCursor_TypedefDecl: output.typedefs ~= code; break;
88 
89                     default: continue;
90                 }
91         }
92 
93         output.structs ~= deferredDeclarations.values;
94         output.externDeclaration = externDeclaration();
95 
96         auto data = output.toString;
97         write(outputFile, data);
98     }
99 
100     string translate (Cursor cursor, Cursor parent = Cursor.empty)
101     {
102         with (CXCursorKind)
103             switch (cursor.kind)
104             {
105                 case CXCursor_ObjCInterfaceDecl:
106                     return (new ObjcInterface!(ClassData)(cursor, parent, this)).translate;
107                 break;
108 
109                 case CXCursor_ObjCProtocolDecl:
110                     return (new ObjcInterface!(InterfaceData)(cursor, parent, this)).translate;
111                 break;
112 
113                 case CXCursor_ObjCCategoryDecl:
114                     return (new Category(cursor, parent, this)).translate;
115                 break;
116 
117                 case CXCursor_VarDecl:
118                 {
119                     auto context = output.newContext();
120                     version (D1)
121                         context ~= "extern ";
122                     else
123                         context ~= "extern __gshared ";
124                     return variable(cursor, context);
125                 }
126                 break;
127 
128                 case CXCursor_FunctionDecl:
129                 {
130                     auto name = translateIdentifier(cursor.spelling);
131                     return translateFunction(cursor.func, name, output) ~ ";";
132                 }
133                 break;
134 
135                 case CXCursor_TypedefDecl:
136                     return typedef_(cursor, output.newContext);
137                 break;
138 
139                 case CXCursor_StructDecl:
140                     auto code = (new Record!(StructData)(cursor, parent, this)).translate;
141                     if (cursor.isDefinition)
142                     {
143                         if (cursor.spelling in deferredDeclarations)
144                             deferredDeclarations.remove(cursor.spelling);
145                         return code;
146                     }
147                     else
148                     {
149                         deferredDeclarations[cursor.spelling] = code;
150                         return "";
151                     }
152                     break;
153                 case CXCursor_EnumDecl: return (new Enum(cursor, parent, this)).translate; break;
154                 case CXCursor_UnionDecl: return (new Record!(UnionData)(cursor, parent, this)).translate; break;
155 
156                 default:
157                     return "";
158                     //assert(0, `Translator.translate: missing implementation for "` ~ cursor.kind.toString ~ `".`);
159             }
160     }
161 
162     string variable (Cursor cursor, String context = null)
163     {
164         if (!context)
165             context = output;
166 
167         context ~= translateType(cursor.type);
168         context ~= " " ~ translateIdentifier(cursor.spelling);
169         context ~= ";";
170 
171         return context.data;
172     }
173 
174     string typedef_ (Cursor cursor, String context = output)
175     {
176         context ~= "alias ";
177         context ~= translateType(cursor.type.canonicalType);
178         context ~= " " ~ cursor.spelling;
179         context ~= ";";
180 
181         return context.data;
182     }
183 
184 private:
185 
186     bool skipDeclaration (Cursor cursor)
187     {
188         return inputFile != cursor.location.spelling.file;
189     }
190 
191     string externDeclaration ()
192     {
193         final switch (language)
194         {
195             case Language.c: return "extern (C):";
196             case Language.objC: return "extern (Objective-C):";
197             // case Language.cpp: return "extern (C++):";
198         }
199     }
200 }
201 
202 string translateFunction (FunctionCursor func, string name, String context, bool isStatic = false)
203 {
204     if (isStatic)
205         context ~= "static ";
206 
207     Parameter[] params;
208 
209     if (func.type.isValid) // This will be invalid of Objective-C methods
210         params.reserve(func.type.func.arguments.length);
211 
212     foreach (param ; func.parameters)
213     {
214         auto type = translateType(param.type);
215         params ~= Parameter(type, param.spelling);
216     }
217 
218     auto resultType = translateType(func.resultType);
219 
220     return translateFunction(resultType, name, params, func.isVariadic, context);
221 }
222 
223 string getAnonymousName (Cursor cursor)
224 {
225     if (auto name = cursor in anonymousNames)
226         return *name;
227 
228     return "";
229 }
230 
231 string generateAnonymousName (Cursor cursor)
232 {
233     auto name = getAnonymousName(cursor);
234 
235     if (name.isBlank)
236     {
237         name = "_Anonymous_" ~ anonymousNames.length.toString;
238         anonymousNames[cursor] = name;
239     }
240 
241     return name;
242 }
243 
244 package struct Parameter
245 {
246     string type;
247     string name;
248     bool isConst;
249 }
250 
251 package string translateFunction (string result, string name, Parameter[] parameters, bool variadic, String context)
252 {
253     context ~= result;
254     context ~= ' ';
255     context ~= name ~ " (";
256 
257     string[] params;
258     params.reserve(parameters.length);
259 
260     foreach (param ; parameters)
261     {
262         string p;
263 
264         version(D1)
265         {
266             p ~= param.type;
267         }
268         else
269         {
270             if (param.isConst)
271                 p ~= "const(";
272 
273             p ~= param.type;
274 
275             if (param.isConst)
276                 p ~= ')';
277         }
278 
279         if (param.name.any)
280             p ~= " " ~ translateIdentifier(param.name);
281 
282         params ~= p;
283     }
284 
285     if (variadic)
286         params ~= "...";
287 
288     context ~= params.join(", ");
289     context ~= ')';
290 
291     return context.data;
292 }
293 
294 string translateIdentifier (string str)
295 {
296     return isDKeyword(str) ? str ~ '_' : str;
297 }
298 
299 string getInclude (Type type)
300 in
301 {
302     assert(type.isValid);
303 }
304 body
305 {
306     return type.declaration.location.spelling.file.name;
307 }
308 
309 void handleInclude (Type type)
310 {
311     includeHandler.addInclude(getInclude(type));
312 }
313 
314 bool isDKeyword (string str)
315 {
316     switch (str)
317     {
318         case "abstract":
319         case "alias":
320         case "align":
321         case "asm":
322         case "assert":
323         case "auto":
324 
325         case "body":
326         case "bool":
327         case "break":
328         case "byte":
329 
330         case "case":
331         case "cast":
332         case "catch":
333         case "cdouble":
334         case "cent":
335         case "cfloat":
336         case "char":
337         case "class":
338         case "const":
339         case "continue":
340         case "creal":
341 
342         case "dchar":
343         case "debug":
344         case "default":
345         case "delegate":
346         case "delete":
347         case "deprecated":
348         case "do":
349         case "double":
350 
351         case "else":
352         case "enum":
353         case "export":
354         case "extern":
355 
356         case "false":
357         case "final":
358         case "finally":
359         case "float":
360         case "for":
361         case "foreach":
362         case "foreach_reverse":
363         case "function":
364 
365         case "goto":
366 
367         case "idouble":
368         case "if":
369         case "ifloat":
370         case "import":
371         case "in":
372         case "inout":
373         case "int":
374         case "interface":
375         case "invariant":
376         case "ireal":
377         case "is":
378 
379         case "lazy":
380         case "long":
381 
382         case "macro":
383         case "mixin":
384         case "module":
385 
386         case "new":
387         case "nothrow":
388         case "null":
389 
390         case "out":
391         case "override":
392 
393         case "package":
394         case "pragma":
395         case "private":
396         case "protected":
397         case "public":
398         case "pure":
399 
400         case "real":
401         case "ref":
402         case "return":
403 
404         case "scope":
405         case "shared":
406         case "short":
407         case "static":
408         case "struct":
409         case "super":
410         case "switch":
411         case "synchronized":
412 
413         case "template":
414         case "this":
415         case "throw":
416         case "true":
417         case "try":
418         case "typedef":
419         case "typeid":
420         case "typeof":
421 
422         case "ubyte":
423         case "ucent":
424         case "uint":
425         case "ulong":
426         case "union":
427         case "unittest":
428         case "ushort":
429 
430         case "version":
431         case "void":
432         case "volatile":
433 
434         case "wchar":
435         case "while":
436         case "with":
437 
438         case "__FILE__":
439         case "__LINE__":
440         case "__DATE__":
441         case "__TIME__":
442         case "__TIMESTAMP__":
443         case "__VENDOR__":
444         case "__VERSION__":
445             return true;
446 
447         default: break;
448     }
449 
450     if (true /*D2*/)
451     {
452         switch (str)
453         {
454             case "immutable":
455             case "nothrow":
456             case "pure":
457             case "shared":
458 
459             case "__gshared":
460             case "__thread":
461             case "__traits":
462 
463             case "__EOF__":
464                 return true;
465 
466             default: return str.any && str.first == '@';
467         }
468     }
469 
470     return false;
471 }
472 
473 enum Language
474 {
475     c,
476     objC
477 // Can't handle C++ yet
478 //    cpp
479 }