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 import std.array;
11 
12 import clang.c.Index;
13 import clang.Cursor;
14 import clang.File;
15 import clang.Index;
16 import clang.TranslationUnit;
17 import clang.Type;
18 import clang.Util;
19 
20 import dstep.core.Exceptions;
21 import dstep.Configuration;
22 
23 import dstep.translator.Context;
24 import dstep.translator.Declaration;
25 import dstep.translator.Enum;
26 import dstep.translator.IncludeHandler;
27 import dstep.translator.objc.Category;
28 import dstep.translator.objc.ObjcInterface;
29 import dstep.translator.Options;
30 import dstep.translator.Output;
31 import dstep.translator.MacroDefinition;
32 import dstep.translator.Record;
33 import dstep.translator.Type;
34 import dstep.translator.TypeInference;
35 
36 public import dstep.translator.Options;
37 
38 class TranslationException : DStepException
39 {
40     this (string message, string file = __FILE__, size_t line = __LINE__)
41     {
42         super(message, file, line);
43     }
44 }
45 
46 class Translator
47 {
48     private
49     {
50         TranslationUnit translationUnit;
51 
52         string outputFile;
53         string inputFilename;
54         File inputFile;
55         Language language;
56         string[string] deferredDeclarations;
57     }
58 
59     TypedMacroDefinition[string] typedMacroDefinitions;
60     Context context;
61 
62     this (TranslationUnit translationUnit, Options options = Options.init)
63     {
64         this.inputFilename = translationUnit.spelling;
65         this.translationUnit = translationUnit;
66         outputFile = options.outputFile;
67         language = options.language;
68 
69         inputFile = translationUnit.file(inputFilename);
70         context = new Context(translationUnit, options, this);
71     }
72 
73     void translate ()
74     {
75         write(outputFile, translateToString());
76     }
77 
78     Output translateCursors()
79     {
80         Output result = new Output(context.commentIndex);
81         typedMacroDefinitions = inferMacroSignatures(context);
82 
83         bool first = true;
84 
85         foreach (cursor, parent; translationUnit.cursor.allInOrder)
86         {
87             if (!skipDeclaration(cursor))
88             {
89                 if (first)
90                 {
91                     if (result.flushHeaderComment())
92                         result.separator();
93 
94                     externDeclaration(result);
95                     first = false;
96                 }
97 
98                 translateInGlobalScope(result, cursor, parent);
99             }
100         }
101 
102         if (context.commentIndex)
103             result.flushLocation(context.commentIndex.queryLastLocation());
104 
105         foreach (value; deferredDeclarations.values)
106             result.singleLine(value);
107 
108         result.finalize();
109 
110         return result;
111     }
112 
113     string translateToString()
114     {
115         import std.algorithm.mutation : strip;
116 
117         Output main = translateCursors();
118         Output head = new Output();
119 
120         moduleDeclaration(head);
121         context.includeHandler.toImports(head);
122 
123         return main.header ~ head.data ~ main.content;
124     }
125 
126     void translateInGlobalScope(
127         Output output,
128         Cursor cursor,
129         Cursor parent = Cursor.empty)
130     {
131         translate(output, cursor, parent);
132 
133         if (!context.globalScope.empty)
134         {
135             output.separator();
136             output.output(context.globalScope);
137             output.separator();
138             context.globalScope.reset();
139         }
140     }
141 
142     void translate (
143         Output output,
144         Cursor cursor,
145         Cursor parent = Cursor.empty)
146     {
147         with (CXCursorKind)
148         {
149             switch (cursor.kind)
150             {
151                 case objCInterfaceDecl:
152                     output.flushLocation(cursor.extent, false);
153                     translateObjCInterfaceDecl(output, cursor, parent);
154                     break;
155 
156                 case objCProtocolDecl:
157                     output.flushLocation(cursor.extent, false);
158                     translateObjCProtocolDecl(output, cursor, parent);
159                     break;
160 
161                 case objCCategoryDecl:
162                     output.flushLocation(cursor.extent, false);
163                     translateObjCCategoryDecl(output, cursor, parent);
164                     break;
165 
166                 case varDecl:
167                     output.flushLocation(cursor.extent);
168                     translateVarDecl(output, cursor, parent);
169                     break;
170 
171                 case functionDecl:
172                     translateFunctionDecl(output, cursor, parent);
173                     break;
174 
175                 case typedefDecl:
176                     translateTypedefDecl(output, cursor);
177                     break;
178 
179                 case structDecl:
180                     translateRecord(output, context, cursor);
181                     break;
182 
183                 case enumDecl:
184                     translateEnum(output, context, cursor);
185                     break;
186 
187                 case unionDecl:
188                     translateRecord(output, context, cursor);
189                     break;
190 
191                 case macroDefinition:
192                     output.flushLocation(cursor.extent);
193                     translateMacroDefinition(output, cursor, parent);
194                     break;
195 
196                 case macroExpansion:
197                     output.flushLocation(cursor.extent);
198                     break;
199 
200                 default:
201                     break;
202             }
203         }
204     }
205 
206     void translateObjCInterfaceDecl(Output output, Cursor cursor, Cursor parent)
207     {
208         (new ObjcInterface!(ClassData)(cursor, parent, this)).translate(output);
209     }
210 
211     void translateObjCProtocolDecl(Output output, Cursor cursor, Cursor parent)
212     {
213         (new ObjcInterface!(InterfaceData)(cursor, parent, this)).translate(output);
214     }
215 
216     void translateObjCCategoryDecl(Output output, Cursor cursor, Cursor parent)
217     {
218         (new Category(cursor, parent, this)).translate(output);
219     }
220 
221     void translateVarDecl(Output output, Cursor cursor, Cursor parent)
222     {
223         version (D1)
224             string storageClass = "extern ";
225         else
226             string storageClass = "extern __gshared ";
227 
228         variable(output, cursor, storageClass);
229     }
230 
231     void translateFunctionDecl(Output output, Cursor cursor, Cursor parent)
232     {
233         output.flushLocation(cursor.extent);
234 
235         immutable auto name = translateIdentifier(cursor.spelling);
236         output.adaptiveSourceNode(translateFunction(context, cursor.func, name));
237         output.append(";");
238     }
239 
240     void declareRecordForTypedef(Output output, Cursor typedef_)
241     {
242         assert(typedef_.kind == CXCursorKind.typedefDecl);
243 
244         auto underlying = typedef_.underlyingCursor();
245 
246         if (underlying.isEmpty)
247             return;
248 
249         if (underlying.isEmpty ||
250             underlying.kind != CXCursorKind.structDecl &&
251             underlying.kind != CXCursorKind.unionDecl)
252             return;
253 
254         if (context.alreadyDefined(underlying.canonical))
255             return;
256 
257         bool skipdef = shouldSkipRecordDefinition(context, underlying);
258 
259         if (underlying.definition.isEmpty || skipdef)
260             translateRecordDecl(output, context, underlying);
261     }
262 
263     bool shouldSkipAlias(Cursor typedef_)
264     {
265         assert(typedef_.kind == CXCursorKind.typedefDecl);
266         return context.options.reduceAliases && typedef_.type.isAliasReducible;
267     }
268 
269     void translateTypedefDecl(Output output, Cursor typedef_)
270     {
271         assert(typedef_.kind == CXCursorKind.typedefDecl);
272 
273         output.flushLocation(typedef_.extent);
274 
275         auto underlying = typedef_.underlyingCursor;
276 
277         if (!context.shouldSkipRecord(underlying) && !shouldSkipAlias(typedef_))
278         {
279             declareRecordForTypedef(output, typedef_);
280 
281             auto typedefp = context.typedefParent(underlying);
282 
283             if (typedef_ != typedefp ||
284                 underlying.isEmpty ||
285                 (underlying.spelling != typedef_.spelling &&
286                 underlying.spelling != ""))
287             {
288                 auto canonical = typedef_.type.canonical;
289 
290                 auto type = translateType(context, typedef_, canonical);
291 
292                 auto typeSpelling = type.makeString();
293 
294                 // Do not alias itself
295                 if (typedef_.spelling != typeSpelling)
296                 {
297                     auto spelling = translateIdentifier(typedef_.spelling);
298 
299                     version (D1)
300                     {
301                         output.adaptiveSourceNode(
302                             type.wrapWith("alias ", " " ~ spelling ~ ";"));
303                     }
304                     else
305                     {
306                         output.adaptiveSourceNode(
307                             type.wrapWith("alias " ~ spelling ~ " = ", ";"));
308                     }
309                 }
310 
311                 context.markAsDefined(typedef_);
312             }
313         }
314     }
315 
316     void translateMacroDefinition(Output output, Cursor cursor, Cursor parent)
317     {
318         if (context.options.translateMacros)
319         {
320             if (auto definition = cursor.spelling in typedMacroDefinitions)
321             {
322                 dstep.translator.MacroDefinition
323                     .translateMacroDefinition(output, context, *definition);
324             }
325         }
326     }
327 
328     void variable (Output output, Cursor cursor, string prefix = "")
329     {
330         translateVariable(output, context, cursor, prefix);
331     }
332 
333 private:
334 
335     bool skipDeclaration (Cursor cursor)
336     {
337         return (inputFilename != "" &&
338             inputFile != cursor.location.spelling.file)
339             || context.options.skipSymbols.contains(cursor.spelling)
340             || cursor.isPredefined;
341     }
342 
343     void moduleDeclaration (Output output)
344     {
345         if (context.options.packageName != "")
346         {
347             output.singleLine("module %s;", fullModuleName(
348                 context.options.packageName,
349                 context.options.outputFile,
350                 context.options.normalizeModules));
351 
352             output.separator();
353         }
354     }
355 
356     void externDeclaration (Output output)
357     {
358         final switch (language)
359         {
360             case Language.c:
361                 output.singleLine("extern (C):");
362                 break;
363 
364             case Language.objC:
365                 output.singleLine("extern (Objective-C):");
366                 break;
367         }
368 
369         foreach (attribute; context.options.globalAttributes)
370             output.singleLine("%s:", attribute);
371 
372         output.separator();
373     }
374 }
375 
376 SourceNode translateFunction (
377     Context context,
378     FunctionCursor func,
379     string name,
380     bool isStatic = false)
381 {
382     bool isVariadic(Context context, size_t numParams, FunctionCursor func)
383     {
384         if (func.isVariadic)
385         {
386             if (context.options.zeroParamIsVararg)
387                 return true;
388             else if (numParams == 0)
389                 return false;
390             else
391                 return true;
392         }
393 
394         return false;
395     }
396 
397     Parameter[] params;
398 
399     if (func.type.isValid) // This will be invalid for Objective-C methods
400         params.reserve(func.type.func.arguments.length);
401 
402     foreach (param ; func.parameters)
403     {
404         auto type = translateType(context, param);
405         params ~= Parameter(type, param.spelling);
406     }
407 
408     auto resultType = translateType(context, func, func.resultType);
409     auto multiline = func.extent.isMultiline &&
410         !context.options.singleLineFunctionSignatures;
411     auto spacer = context.options.spaceAfterFunctionName ? " " : "";
412 
413     return translateFunction(
414         resultType,
415         name,
416         params,
417         isVariadic(context, params.length, func),
418         isStatic ? "static " : "",
419         spacer,
420         multiline);
421 }
422 
423 package struct Parameter
424 {
425     SourceNode type;
426     string name;
427     bool isConst;
428 }
429 
430 package SourceNode translateFunction (
431     SourceNode resultType,
432     string name,
433     Parameter[] parameters,
434     bool variadic,
435     string prefix = "",
436     string spacer = " ",
437     bool multiline = false)
438 {
439     import std.format : format;
440 
441     string[] params;
442     params.reserve(parameters.length);
443 
444     foreach (param ; parameters)
445     {
446         string p;
447 
448         version(D1)
449         {
450             p ~= param.type;
451         }
452         else
453         {
454             if (param.isConst)
455                 p ~= "const(";
456 
457             p ~= param.type.makeString();
458 
459             if (param.isConst)
460                 p ~= ')';
461         }
462 
463         if (param.name.length)
464             p ~= " " ~ translateIdentifier(param.name);
465 
466         params ~= p;
467     }
468 
469     if (variadic)
470         params ~= "...";
471 
472     auto result = makeSourceNode(
473         format("%s%s %s%s(", prefix, resultType.makeString(), name, spacer),
474         params,
475         ",",
476         ")");
477 
478     return multiline ? result : result.flatten();
479 }
480 
481 void translateVariable (Output output, Context context, Cursor cursor, string prefix = "")
482 {
483     if (!context.alreadyDefined(cursor.canonical))
484     {
485         auto type = translateType(context, cursor, cursor.type);
486         auto identifier = translateIdentifier(cursor.spelling);
487         output.adaptiveSourceNode(type.wrapWith(prefix, " " ~ identifier ~ ";"));
488         context.markAsDefined(cursor.canonical);
489     }
490 }
491 
492 void handleInclude (Context context, Type type)
493 {
494     import std.algorithm.searching;
495     import std.path;
496 
497     if (!context.includeHandler.resolveDependency(type.declaration)) {
498         context.includeHandler.addInclude(type.declaration.path);
499     }
500 }
501 
502 bool isDKeyword (string str)
503 {
504     switch (str)
505     {
506         case "abstract":
507         case "alias":
508         case "align":
509         case "asm":
510         case "assert":
511         case "auto":
512 
513         case "body":
514         case "bool":
515         case "break":
516         case "byte":
517 
518         case "case":
519         case "cast":
520         case "catch":
521         case "cdouble":
522         case "cent":
523         case "cfloat":
524         case "char":
525         case "class":
526         case "const":
527         case "continue":
528         case "creal":
529 
530         case "dchar":
531         case "debug":
532         case "default":
533         case "delegate":
534         case "delete":
535         case "deprecated":
536         case "do":
537         case "double":
538 
539         case "else":
540         case "enum":
541         case "export":
542         case "extern":
543 
544         case "false":
545         case "final":
546         case "finally":
547         case "float":
548         case "for":
549         case "foreach":
550         case "foreach_reverse":
551         case "function":
552 
553         case "goto":
554 
555         case "idouble":
556         case "if":
557         case "ifloat":
558         case "import":
559         case "in":
560         case "inout":
561         case "int":
562         case "interface":
563         case "invariant":
564         case "ireal":
565         case "is":
566 
567         case "lazy":
568         case "long":
569 
570         case "macro":
571         case "mixin":
572         case "module":
573 
574         case "new":
575         case "nothrow":
576         case "null":
577 
578         case "out":
579         case "override":
580 
581         case "package":
582         case "pragma":
583         case "private":
584         case "protected":
585         case "public":
586         case "pure":
587 
588         case "real":
589         case "ref":
590         case "return":
591 
592         case "scope":
593         case "shared":
594         case "short":
595         case "static":
596         case "struct":
597         case "super":
598         case "switch":
599         case "synchronized":
600 
601         case "template":
602         case "this":
603         case "throw":
604         case "true":
605         case "try":
606         case "typedef":
607         case "typeid":
608         case "typeof":
609 
610         case "ubyte":
611         case "ucent":
612         case "uint":
613         case "ulong":
614         case "union":
615         case "unittest":
616         case "ushort":
617 
618         case "version":
619         case "void":
620         case "volatile":
621 
622         case "wchar":
623         case "while":
624         case "with":
625 
626         case "__FILE__":
627         case "__LINE__":
628         case "__DATE__":
629         case "__TIME__":
630         case "__TIMESTAMP__":
631         case "__VENDOR__":
632         case "__VERSION__":
633             return true;
634 
635         default: break;
636     }
637 
638     if (true /*D2*/)
639     {
640         switch (str)
641         {
642             case "immutable":
643             case "nothrow":
644             case "pure":
645             case "shared":
646 
647             case "__gshared":
648             case "__thread":
649             case "__traits":
650 
651             case "__EOF__":
652                 return true;
653 
654             default: return str.length && str[0] == '@';
655         }
656     }
657 
658     return false;
659 }
660 
661 string renameDKeyword (string str)
662 {
663     return str ~ '_';
664 }
665 
666 string translateIdentifier (string str)
667 {
668     return isDKeyword(str) ? renameDKeyword(str) : str;
669 }