1 /**
2    Cursor translations
3  */
4 module dpp.translation.translation;
5 
6 import dpp.from;
7 
8 
9 alias Translator = string[] function(
10     in from!"clang".Cursor cursor,
11     ref from!"dpp.runtime.context".Context context,
12 ) @safe;
13 
14 string translateTopLevelCursor(in from!"clang".Cursor cursor,
15                                ref from!"dpp.runtime.context".Context context,
16                                in string file = __FILE__,
17                                in size_t line = __LINE__)
18     @safe
19 {
20     import std.array: join;
21     import std.algorithm: map;
22 
23     return cursor.skipTopLevel
24         ? ""
25         : translate(cursor, context, file, line).map!(a => "    " ~ a).join("\n");
26 }
27 
28 private bool skipTopLevel(in from!"clang".Cursor cursor) @safe pure {
29     import dpp.translation.aggregate: isAggregateC;
30     import clang: Cursor;
31     import std.algorithm: startsWith, canFind;
32 
33     // We want to ignore anonymous structs and unions but not enums. See #54
34     if(cursor.spelling == "" && cursor.kind == Cursor.Kind.EnumDecl)
35         return false;
36 
37     // don't bother translating top-level anonymous aggregates
38     if(isAggregateC(cursor) && cursor.spelling == "")
39         return true;
40 
41     static immutable forbiddenSpellings =
42         [
43             "ulong", "ushort", "uint",
44             "va_list", "__gnuc_va_list",
45             "_IO_2_1_stdin_", "_IO_2_1_stdout_", "_IO_2_1_stderr_",
46         ];
47 
48     return forbiddenSpellings.canFind(cursor.spelling) ||
49         cursor.isPredefined ||
50         cursor.kind == Cursor.Kind.MacroExpansion
51         ;
52 }
53 
54 
55 string[] translate(in from!"clang".Cursor cursor,
56                    ref from!"dpp.runtime.context".Context context,
57                    in string file = __FILE__,
58                    in size_t line = __LINE__)
59     @safe
60 {
61     import dpp.runtime.context: Language;
62     import std.conv: text;
63     import std.algorithm: canFind;
64 
65     debugCursor(cursor, context);
66 
67     if(context.language == Language.Cpp && ignoredCppCursorSpellings.canFind(cursor.spelling)) {
68         return [];
69     }
70 
71     
72     if(cursor.kind !in translators) {
73         if(context.options.hardFail)
74             throw new Exception(text("Cannot translate unknown cursor kind ", cursor.kind),
75                                 file,
76                                 line);
77         else
78             return [];
79     }
80 
81     const indentation = context.indentation;
82     scope(exit) context.setIndentation(indentation);
83     context.indent;
84 
85     try
86         return translators[cursor.kind](cursor, context);
87     catch(Exception e) {
88         import std.stdio: stderr;
89         debug {
90             () @trusted {
91                 stderr.writeln("\nCould not translate cursor ", cursor,
92                                " sourceRange: ", cursor.sourceRange,
93                                " children: ", cursor.children, "\n");
94             }();
95         }
96         throw e;
97     }
98 }
99 
100 void debugCursor(in from!"clang".Cursor cursor,
101                  in from!"dpp.runtime.context".Context context)
102     @safe
103 {
104     import clang: Cursor;
105     import std.algorithm: startsWith, canFind;
106 
107     version(unittest) {}
108     else if(!context.debugOutput) return;
109 
110     const isMacro = cursor.kind == Cursor.Kind.MacroDefinition;
111     const isOkMacro =
112         !cursor.spelling.startsWith("__") &&
113         !["_LP64", "unix", "linux"].canFind(cursor.spelling);
114     const canonical = cursor.isCanonical ? " CAN" : "";
115     const definition = cursor.isDefinition ? " DEF" : "";
116 
117     if(!isMacro || isOkMacro) {
118         context.log(cursor, canonical, definition, " @ ", cursor.sourceRange);
119     }
120 }
121 
122 Translator[from!"clang".Cursor.Kind] translators() @safe {
123     import dpp.translation;
124     import clang: Cursor;
125 
126     static string[] ignore(
127         in Cursor cursor,
128         ref from!"dpp.runtime.context".Context context)
129     {
130         return [];
131     }
132 
133     static string[] translateUnexposed(
134         in Cursor cursor,
135         ref from!"dpp.runtime.context".Context context)
136     {
137         import clang: Type;
138         import std.conv: text;
139 
140         switch(cursor.type.kind) with(Type.Kind) {
141             default:
142                 throw new Exception(text("Unknown unexposed declaration type ", cursor.type));
143             case Invalid:
144                 return [];
145         }
146         assert(0);
147     }
148 
149     static string[] translateAccess(
150         in Cursor cursor,
151         ref from!"dpp.runtime.context".Context context)
152     {
153         import clang: AccessSpecifier;
154 
155         final switch(cursor.accessSpecifier) with(AccessSpecifier) {
156             case InvalidAccessSpecifier: assert(0);
157             case Public: return ["    public:"];
158             case Protected: return ["    protected:"];
159             case Private: return ["    private:"];
160         }
161 
162         assert(0);
163     }
164 
165     with(Cursor.Kind) {
166         return [
167             ClassDecl:                          &translateClass,
168             StructDecl:                         &translateStruct,
169             UnionDecl:                          &translateUnion,
170             EnumDecl:                           &translateEnum,
171             FunctionDecl:                       &translateFunction,
172             FieldDecl:                          &translateField,
173             TypedefDecl:                        &translateTypedef,
174             MacroDefinition:                    &translateMacro,
175             InclusionDirective:                 &ignore,
176             EnumConstantDecl:                   &translateEnumConstant,
177             VarDecl:                            &translateVariable,
178             UnexposedDecl:                      &translateUnexposed,
179             CXXAccessSpecifier:                 &translateAccess,
180             CXXMethod:                          &translateFunction,
181             Constructor:                        &translateFunction,
182             Destructor:                         &translateFunction,
183             TypeAliasDecl:                      &translateTypedef,
184             ClassTemplate:                      &translateClass,
185             TemplateTypeParameter:              &ignore,
186             NonTypeTemplateParameter:           &ignore,
187             ConversionFunction:                 &translateFunction,
188             Namespace:                          &translateNamespace,
189             VisibilityAttr:                     &ignore, // ???
190             FirstAttr:                          &ignore, // ???
191             ClassTemplatePartialSpecialization: &translateClass,
192         ];
193     }
194 }
195 
196 // blacklist of cursors in the C++ standard library that dpp can't handle
197 string[] ignoredCppCursorSpellings() @safe pure nothrow {
198     return
199         [
200             "is_function",
201             "__is_referenceable",
202             "__is_convertible_helper",
203             "aligned_union",
204             "__expanded_common_type_wrapper",
205             "underlying_type",
206             "__result_of_memfun_ref",
207             "__result_of_memfun_deref",
208             "__result_of_memfun",
209             "__result_of_impl",
210             "result_of",
211             "__detector",
212             "__is_swappable_with_impl",
213             "__is_nothrow_swappable_with_impl",
214             "is_rvalue_reference",
215             "__is_member_pointer_helper",
216             "__do_is_implicitly_default_constructible_impl",
217             "remove_reference",
218             "remove_extent",  // FIXME
219             "remove_all_extents",  // FIXME
220             "__remove_pointer_helper", // FIXME
221             "__result_of_memobj",
222             "__nonesuch",  // FIXME (struct default ctor)
223         ];
224 }