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 skipTopLevel(cursor, context) 24 ? "" 25 : translate(cursor, context, file, line).map!(a => " " ~ a).join("\n"); 26 } 27 28 private bool skipTopLevel(in from!"clang".Cursor cursor, 29 in from!"dpp.runtime.context".Context context) 30 @safe pure 31 { 32 import dpp.translation.aggregate: isAggregateC; 33 import clang: Cursor; 34 import std.algorithm: startsWith, canFind; 35 36 // We want to ignore anonymous structs and unions but not enums. See #54 37 if(cursor.spelling == "" && cursor.kind == Cursor.Kind.EnumDecl) 38 return false; 39 40 // don't bother translating top-level anonymous aggregates 41 if(isAggregateC(cursor) && cursor.spelling == "") 42 return true; 43 44 if(context.options.ignoreMacros && cursor.kind == Cursor.Kind.MacroDefinition) 45 return true; 46 47 static immutable forbiddenSpellings = 48 [ 49 "ulong", "ushort", "uint", 50 "va_list", "__gnuc_va_list", 51 "_IO_2_1_stdin_", "_IO_2_1_stdout_", "_IO_2_1_stderr_", 52 ]; 53 54 return forbiddenSpellings.canFind(cursor.spelling) || 55 cursor.isPredefined || 56 cursor.kind == Cursor.Kind.MacroExpansion 57 ; 58 } 59 60 61 string[] translate(in from!"clang".Cursor cursor, 62 ref from!"dpp.runtime.context".Context context, 63 in string file = __FILE__, 64 in size_t line = __LINE__) 65 @safe 66 { 67 import dpp.runtime.context: Language; 68 import dpp.translation.exception: UntranslatableException; 69 import std.conv: text; 70 import std.algorithm: canFind, any; 71 import std.array: join; 72 73 debugCursor(cursor, context); 74 75 if(context.language == Language.Cpp && ignoredCppCursorSpellings.canFind(cursor.spelling)) { 76 return []; 77 } 78 79 if(context.options.ignoredCursors.canFind(cursor.spelling)) { 80 return []; 81 } 82 83 if(cursor.kind !in translators) { 84 if(context.options.hardFail) 85 throw new Exception(text("Cannot translate unknown cursor kind ", cursor.kind), 86 file, 87 line); 88 else 89 return []; 90 } 91 92 const indentation = context.indentation; 93 scope(exit) context.setIndentation(indentation); 94 context.indent; 95 96 try { 97 auto lines = translators[cursor.kind](cursor, context); 98 99 if(lines.any!untranslatable) 100 throw new UntranslatableException( 101 text("Not valid D:\n", 102 "------------\n", 103 lines.join("\n"), 104 "\n------------\n",)); 105 106 return lines; 107 } catch(UntranslatableException e) { 108 109 debug { 110 import std.stdio: stderr; 111 () @trusted { 112 if(context.options.detailedUntranslatable) 113 stderr.writeln("\nUntranslatable cursor ", cursor, 114 "\nmsg: ", e.msg, 115 "\nsourceRange: ", cursor.sourceRange, 116 "\nchildren: ", cursor.children, 117 "\n"); 118 else 119 stderr.writeln("Untranslatable cursor ", cursor); 120 }(); 121 } 122 123 if(context.options.hardFail) 124 throw e; 125 else 126 return []; 127 128 } catch(Exception e) { 129 130 debug { 131 import std.stdio: stderr; 132 () @trusted { 133 stderr.writeln("\nCould not translate cursor ", cursor, 134 "\nmsg: ", e.msg, 135 "\nsourceRange: ", cursor.sourceRange, 136 "\nchildren: ", cursor.children, "\n"); 137 }(); 138 } 139 140 throw e; 141 } 142 } 143 144 void debugCursor(in from!"clang".Cursor cursor, 145 in from!"dpp.runtime.context".Context context) 146 @safe 147 { 148 import clang: Cursor; 149 import std.algorithm: startsWith, canFind; 150 151 version(unittest) {} 152 else if(!context.debugOutput) return; 153 154 const isMacro = cursor.kind == Cursor.Kind.MacroDefinition; 155 const isOkMacro = 156 !cursor.spelling.startsWith("__") && 157 !cursor.spelling.startsWith("_GLIBCXX") && 158 !["_LP64", "unix", "linux"].canFind(cursor.spelling); 159 const canonical = cursor.isCanonical ? " CAN" : ""; 160 const definition = cursor.isDefinition ? " DEF" : ""; 161 162 if(!isMacro || isOkMacro) { 163 context.log(cursor, canonical, definition, " @ ", cursor.sourceRange); 164 } 165 } 166 167 Translator[from!"clang".Cursor.Kind] translators() @safe { 168 import dpp.translation; 169 import clang: Cursor; 170 171 static string[] ignore( 172 in Cursor cursor, 173 ref from!"dpp.runtime.context".Context context) 174 { 175 return []; 176 } 177 178 static string[] translateUnexposed( 179 in Cursor cursor, 180 ref from!"dpp.runtime.context".Context context) 181 { 182 import clang: Type; 183 import std.conv: text; 184 185 switch(cursor.type.kind) with(Type.Kind) { 186 default: 187 throw new Exception(text("Unknown unexposed declaration type ", cursor.type)); 188 case Invalid: 189 return []; 190 } 191 assert(0); 192 } 193 194 static string[] translateAccess( 195 in Cursor cursor, 196 ref from!"dpp.runtime.context".Context context) 197 { 198 import clang: AccessSpecifier; 199 200 context.accessSpecifier = cursor.accessSpecifier; 201 202 final switch(cursor.accessSpecifier) with(AccessSpecifier) { 203 case InvalidAccessSpecifier: assert(0); 204 case Public: return [" public:"]; 205 case Protected: return [" protected:"]; 206 case Private: return [" private:"]; 207 } 208 209 assert(0); 210 } 211 212 with(Cursor.Kind) { 213 return [ 214 ClassDecl: &translateClass, 215 StructDecl: &translateStruct, 216 UnionDecl: &translateUnion, 217 EnumDecl: &translateEnum, 218 FunctionDecl: &translateFunction, 219 FieldDecl: &translateField, 220 TypedefDecl: &translateTypedef, 221 MacroDefinition: &translateMacro, 222 InclusionDirective: &ignore, 223 EnumConstantDecl: &translateEnumConstant, 224 VarDecl: &translateVariable, 225 UnexposedDecl: &translateUnexposed, 226 CXXAccessSpecifier: &translateAccess, 227 CXXMethod: &translateFunction, 228 Constructor: &translateFunction, 229 Destructor: &translateFunction, 230 TypeAliasDecl: &translateTypedef, 231 ClassTemplate: &translateClass, 232 TemplateTypeParameter: &ignore, 233 NonTypeTemplateParameter: &ignore, 234 ConversionFunction: &translateFunction, 235 Namespace: &translateNamespace, 236 VisibilityAttr: &ignore, // ??? 237 // FirstAttr appears when there are compiler-specific attributes on a type 238 FirstAttr: &ignore, 239 ClassTemplatePartialSpecialization: &translateClass, 240 TypeAliasTemplateDecl: &translateTypeAliasTemplate, 241 FunctionTemplate: &translateFunction, 242 // For ParmDecl, see it.cpp.opaque.std::function 243 ParmDecl: &ignore, 244 ]; 245 } 246 } 247 248 249 // if this translated line can't be valid D code 250 bool untranslatable(in string line) @safe pure { 251 import std.algorithm: canFind; 252 return 253 line.canFind(`&)`) 254 || line.canFind("&,") 255 || line.canFind("&...") 256 || line.canFind(" (*)") 257 || line.canFind("variant!") 258 || line.canFind("value _ ") 259 || line.canFind("enable_if_c") 260 || line.canFind(`(this_)_M_t._M_equal_range_tr(`) 261 || line.canFind(`this-`) 262 || line.canFind("_BoundArgs...") 263 || line.canFind("sizeof...") 264 || line.canFind("template<") // FIXME: mir_slice 265 ; 266 } 267 268 269 // blacklist of cursors in the C++ standard library that dpp can't handle 270 private string[] ignoredCppCursorSpellings() @safe pure nothrow { 271 return 272 [ 273 "is_function", // dmd bug 274 "is_const", 275 "is_volatile", 276 "allocator_traits", // FIXME 277 "pair", // FIXME 278 "underlying_type", 279 "underlying_type_t", 280 "result_of", 281 "result_of_t", 282 "pointer_traits", // FIXME 283 "iterator_traits", // FIXME 284 "piecewise_construct", // FIXME 285 "is_rvalue_reference", 286 "remove_reference", 287 "remove_reference_t", 288 "remove_extent", // FIXME 289 "remove_extent_t", // FIXME 290 "remove_all_extents", // FIXME 291 "remove_all_extents_t", // FIXME 292 293 // derives from std::iterator, which is untranslatable due to it taking a 294 // reference template parameter 295 "_Bit_iterator_base", 296 "_Bit_iterator", 297 "_Bit_const_iterator", 298 // needs _Bit_iterator and co 299 "_Bvector_base", 300 ]; 301 }