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