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 dpp.translation.exception: UntranslatableException;
63     import std.conv: text;
64     import std.algorithm: canFind;
65 
66     debugCursor(cursor, context);
67 
68     if(context.language == Language.Cpp && ignoredCppCursorSpellings.canFind(cursor.spelling)) {
69         return [];
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(UntranslatableException e) {
88 
89         debug {
90             import std.stdio: stderr;
91             () @trusted {
92                 stderr.writeln("\nUntranslatable cursor ", cursor,
93                                " sourceRange: ", cursor.sourceRange,
94                                " children: ", cursor.children, "\n");
95             }();
96         }
97 
98         if(context.options.hardFail)
99             throw e;
100         else
101             return [];
102 
103     } catch(Exception e) {
104 
105         debug {
106             import std.stdio: stderr;
107             () @trusted {
108                 stderr.writeln("\nCould not translate cursor ", cursor,
109                                " sourceRange: ", cursor.sourceRange,
110                                " children: ", cursor.children, "\n");
111             }();
112         }
113 
114         throw e;
115     }
116 }
117 
118 void debugCursor(in from!"clang".Cursor cursor,
119                  in from!"dpp.runtime.context".Context context)
120     @safe
121 {
122     import clang: Cursor;
123     import std.algorithm: startsWith, canFind;
124 
125     version(unittest) {}
126     else if(!context.debugOutput) return;
127 
128     const isMacro = cursor.kind == Cursor.Kind.MacroDefinition;
129     const isOkMacro =
130         !cursor.spelling.startsWith("__") &&
131         !["_LP64", "unix", "linux"].canFind(cursor.spelling);
132     const canonical = cursor.isCanonical ? " CAN" : "";
133     const definition = cursor.isDefinition ? " DEF" : "";
134 
135     if(!isMacro || isOkMacro) {
136         context.log(cursor, canonical, definition, " @ ", cursor.sourceRange);
137     }
138 }
139 
140 Translator[from!"clang".Cursor.Kind] translators() @safe {
141     import dpp.translation;
142     import clang: Cursor;
143 
144     static string[] ignore(
145         in Cursor cursor,
146         ref from!"dpp.runtime.context".Context context)
147     {
148         return [];
149     }
150 
151     static string[] translateUnexposed(
152         in Cursor cursor,
153         ref from!"dpp.runtime.context".Context context)
154     {
155         import clang: Type;
156         import std.conv: text;
157 
158         switch(cursor.type.kind) with(Type.Kind) {
159             default:
160                 throw new Exception(text("Unknown unexposed declaration type ", cursor.type));
161             case Invalid:
162                 return [];
163         }
164         assert(0);
165     }
166 
167     static string[] translateAccess(
168         in Cursor cursor,
169         ref from!"dpp.runtime.context".Context context)
170     {
171         import clang: AccessSpecifier;
172 
173         final switch(cursor.accessSpecifier) with(AccessSpecifier) {
174             case InvalidAccessSpecifier: assert(0);
175             case Public: return ["    public:"];
176             case Protected: return ["    protected:"];
177             case Private: return ["    private:"];
178         }
179 
180         assert(0);
181     }
182 
183     with(Cursor.Kind) {
184         return [
185             ClassDecl:                          &translateClass,
186             StructDecl:                         &translateStruct,
187             UnionDecl:                          &translateUnion,
188             EnumDecl:                           &translateEnum,
189             FunctionDecl:                       &translateFunction,
190             FieldDecl:                          &translateField,
191             TypedefDecl:                        &translateTypedef,
192             MacroDefinition:                    &translateMacro,
193             InclusionDirective:                 &ignore,
194             EnumConstantDecl:                   &translateEnumConstant,
195             VarDecl:                            &translateVariable,
196             UnexposedDecl:                      &translateUnexposed,
197             CXXAccessSpecifier:                 &translateAccess,
198             CXXMethod:                          &translateFunction,
199             Constructor:                        &translateFunction,
200             Destructor:                         &translateFunction,
201             TypeAliasDecl:                      &translateTypedef,
202             ClassTemplate:                      &translateClass,
203             TemplateTypeParameter:              &ignore,
204             NonTypeTemplateParameter:           &ignore,
205             ConversionFunction:                 &translateFunction,
206             Namespace:                          &translateNamespace,
207             VisibilityAttr:                     &ignore, // ???
208             FirstAttr:                          &ignore, // ???
209             ClassTemplatePartialSpecialization: &translateClass,
210         ];
211     }
212 }
213 
214 // blacklist of cursors in the C++ standard library that dpp can't handle
215 string[] ignoredCppCursorSpellings() @safe pure nothrow {
216     return
217         [
218             "is_function",  // dmd bug
219             "__is_referenceable",
220             "__is_convertible_helper",
221             "aligned_union",
222             "__expanded_common_type_wrapper",
223             "underlying_type",
224             "__result_of_memfun_ref",
225             "__result_of_memfun_deref",
226             "__result_of_memfun",
227             "__result_of_impl",
228             "result_of",
229             "__detector",
230             "__is_swappable_with_impl",
231             "__is_nothrow_swappable_with_impl",
232             "is_rvalue_reference",
233             "__is_member_pointer_helper",
234             "__do_is_implicitly_default_constructible_impl",
235             "remove_reference",
236             "remove_extent",  // FIXME
237             "remove_all_extents",  // FIXME
238             "__remove_pointer_helper", // FIXME
239             "__result_of_memobj",
240             "piecewise_construct_t",  // FIXME (@disable ctor)
241             "piecewise_construct",  // FIXME (piecewise_construct_t)
242 
243             "hash", // FIXME (stl_bvector.h partial template specialisation)
244             "__is_fast_hash",  // FIXME (hash)
245 
246             "move_iterator",  // FIXME (extra type parameters)
247             "__replace_first_arg", // FIXME
248             "pointer_traits", // FIXME
249             "pair",  // FIXME
250             "iterator_traits",  // FIXME
251 
252             "_Hash_bytes",  // FIXME (std.size_t)
253             "_Fnv_hash_bytes", // FIXME (std.size_t)
254             "allocator_traits",  // FIXME
255             "__allocator_traits_base",  // FIXME
256         ];
257 }