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             if (underlying.isEmpty ||
282                 (underlying.spelling != typedef_.spelling &&
283                 underlying.spelling != ""))
284             {
285                 auto canonical = typedef_.type.canonical;
286                 auto spelling = typedef_.spelling;
287                 auto type = translateType(context, typedef_, canonical);
288 
289                 version (D1)
290                 {
291                     output.adaptiveSourceNode(
292                         type.wrapWith("alias ", " " ~ spelling ~ ";"));
293                 }
294                 else
295                 {
296                     output.adaptiveSourceNode(
297                         type.wrapWith("alias " ~ spelling ~ " = ", ";"));
298                 }
299 
300                 context.markAsDefined(typedef_);
301             }
302         }
303     }
304 
305     void translateMacroDefinition(Output output, Cursor cursor, Cursor parent)
306     {
307         if (context.options.translateMacros)
308         {
309             if (auto definition = cursor.spelling in typedMacroDefinitions)
310             {
311                 dstep.translator.MacroDefinition
312                     .translateMacroDefinition(output, context, *definition);
313             }
314         }
315     }
316 
317     void variable (Output output, Cursor cursor, string prefix = "")
318     {
319         translateVariable(output, context, cursor, prefix);
320     }
321 
322 private:
323 
324     bool skipDeclaration (Cursor cursor)
325     {
326         return (inputFilename != "" &&
327             inputFile != cursor.location.spelling.file)
328             || context.options.skipSymbols.contains(cursor.spelling)
329             || cursor.isPredefined;
330     }
331 
332     void moduleDeclaration (Output output)
333     {
334         if (context.options.packageName != "")
335         {
336             output.singleLine("module %s;", fullModuleName(
337                 context.options.packageName,
338                 context.options.outputFile,
339                 context.options.normalizeModules));
340 
341             output.separator();
342         }
343     }
344 
345     void externDeclaration (Output output)
346     {
347         final switch (language)
348         {
349             case Language.c:
350                 output.singleLine("extern (C):");
351                 break;
352 
353             case Language.objC:
354                 output.singleLine("extern (Objective-C):");
355                 break;
356         }
357 
358         foreach (attribute; context.options.globalAttributes)
359             output.singleLine("%s:", attribute);
360 
361         output.separator();
362     }
363 }
364 
365 SourceNode translateFunction (
366     Context context,
367     FunctionCursor func,
368     string name,
369     bool isStatic = false)
370 {
371     bool isVariadic(Context context, size_t numParams, FunctionCursor func)
372     {
373         if (func.isVariadic)
374         {
375             if (context.options.zeroParamIsVararg)
376                 return true;
377             else if (numParams == 0)
378                 return false;
379             else
380                 return true;
381         }
382 
383         return false;
384     }
385 
386     Parameter[] params;
387 
388     if (func.type.isValid) // This will be invalid for Objective-C methods
389         params.reserve(func.type.func.arguments.length);
390 
391     foreach (param ; func.parameters)
392     {
393         auto type = translateType(context, param);
394         params ~= Parameter(type, param.spelling);
395     }
396 
397     auto resultType = translateType(context, func, func.resultType);
398     auto multiline = func.extent.isMultiline &&
399         !context.options.singleLineFunctionSignatures;
400     auto spacer = context.options.spaceAfterFunctionName ? " " : "";
401 
402     return translateFunction(
403         resultType,
404         name,
405         params,
406         isVariadic(context, params.length, func),
407         isStatic ? "static " : "",
408         spacer,
409         multiline);
410 }
411 
412 package struct Parameter
413 {
414     SourceNode type;
415     string name;
416     bool isConst;
417 }
418 
419 package SourceNode translateFunction (
420     SourceNode resultType,
421     string name,
422     Parameter[] parameters,
423     bool variadic,
424     string prefix = "",
425     string spacer = " ",
426     bool multiline = false)
427 {
428     import std.format : format;
429 
430     string[] params;
431     params.reserve(parameters.length);
432 
433     foreach (param ; parameters)
434     {
435         string p;
436 
437         version(D1)
438         {
439             p ~= param.type;
440         }
441         else
442         {
443             if (param.isConst)
444                 p ~= "const(";
445 
446             p ~= param.type.makeString();
447 
448             if (param.isConst)
449                 p ~= ')';
450         }
451 
452         if (param.name.length)
453             p ~= " " ~ translateIdentifier(param.name);
454 
455         params ~= p;
456     }
457 
458     if (variadic)
459         params ~= "...";
460 
461     auto result = makeSourceNode(
462         format("%s%s %s%s(", prefix, resultType.makeString(), name, spacer),
463         params,
464         ",",
465         ")");
466 
467     return multiline ? result : result.flatten();
468 }
469 
470 void translateVariable (Output output, Context context, Cursor cursor, string prefix = "")
471 {
472     if (!context.alreadyDefined(cursor.canonical))
473     {
474         auto type = translateType(context, cursor, cursor.type);
475         auto identifier = translateIdentifier(cursor.spelling);
476         output.adaptiveSourceNode(type.wrapWith(prefix, " " ~ identifier ~ ";"));
477         context.markAsDefined(cursor.canonical);
478     }
479 }
480 
481 string translateIdentifier (string str)
482 {
483     return isDKeyword(str) ? str ~ '_' : str;
484 }
485 
486 void handleInclude (Context context, Type type)
487 {
488     import std.algorithm.searching;
489     import std.path;
490 
491     if (type.kind == CXTypeKind.typedef_
492         && type.spelling == "time_t"
493         && type.declaration.path.asNormalizedPath.array.endsWith("sys\\types.h"))
494         context.includeHandler.addInclude("time.h");
495     else
496         context.includeHandler.addInclude(type.declaration.path);
497 }
498 
499 bool isDKeyword (string str)
500 {
501     switch (str)
502     {
503         case "abstract":
504         case "alias":
505         case "align":
506         case "asm":
507         case "assert":
508         case "auto":
509 
510         case "body":
511         case "bool":
512         case "break":
513         case "byte":
514 
515         case "case":
516         case "cast":
517         case "catch":
518         case "cdouble":
519         case "cent":
520         case "cfloat":
521         case "char":
522         case "class":
523         case "const":
524         case "continue":
525         case "creal":
526 
527         case "dchar":
528         case "debug":
529         case "default":
530         case "delegate":
531         case "delete":
532         case "deprecated":
533         case "do":
534         case "double":
535 
536         case "else":
537         case "enum":
538         case "export":
539         case "extern":
540 
541         case "false":
542         case "final":
543         case "finally":
544         case "float":
545         case "for":
546         case "foreach":
547         case "foreach_reverse":
548         case "function":
549 
550         case "goto":
551 
552         case "idouble":
553         case "if":
554         case "ifloat":
555         case "import":
556         case "in":
557         case "inout":
558         case "int":
559         case "interface":
560         case "invariant":
561         case "ireal":
562         case "is":
563 
564         case "lazy":
565         case "long":
566 
567         case "macro":
568         case "mixin":
569         case "module":
570 
571         case "new":
572         case "nothrow":
573         case "null":
574 
575         case "out":
576         case "override":
577 
578         case "package":
579         case "pragma":
580         case "private":
581         case "protected":
582         case "public":
583         case "pure":
584 
585         case "real":
586         case "ref":
587         case "return":
588 
589         case "scope":
590         case "shared":
591         case "short":
592         case "static":
593         case "struct":
594         case "super":
595         case "switch":
596         case "synchronized":
597 
598         case "template":
599         case "this":
600         case "throw":
601         case "true":
602         case "try":
603         case "typedef":
604         case "typeid":
605         case "typeof":
606 
607         case "ubyte":
608         case "ucent":
609         case "uint":
610         case "ulong":
611         case "union":
612         case "unittest":
613         case "ushort":
614 
615         case "version":
616         case "void":
617         case "volatile":
618 
619         case "wchar":
620         case "while":
621         case "with":
622 
623         case "__FILE__":
624         case "__LINE__":
625         case "__DATE__":
626         case "__TIME__":
627         case "__TIMESTAMP__":
628         case "__VENDOR__":
629         case "__VERSION__":
630             return true;
631 
632         default: break;
633     }
634 
635     if (true /*D2*/)
636     {
637         switch (str)
638         {
639             case "immutable":
640             case "nothrow":
641             case "pure":
642             case "shared":
643 
644             case "__gshared":
645             case "__thread":
646             case "__traits":
647 
648             case "__EOF__":
649                 return true;
650 
651             default: return str.length && str[0] == '@';
652         }
653     }
654 
655     return false;
656 }